Odd behavior with reactive inputs/outputs and reading/writing values from file
See original GitHub issueI re-created an odd issue I just ran into. Here’s my reproducible example:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html
def generate_inputs():
print('getting value!')
f = open('./value.txt', 'r')
current_value = str(f.read())
f.close()
print('current_value: ' + str(current_value))
input_box = [html.Label(html.Strong('value to write; currently: ')),
dcc.Input(type='number', value=current_value, id='input_value')]
return input_box
app = dash.Dash()
app.layout = html.Div([
html.H2('debug: getting and setting'),
html.Div(
generate_inputs()
),
html.Div(id='file_value_written')
]) # app.layout
@app.callback(
dash.dependencies.Output(component_id='file_value_written', component_property='children'),
[dash.dependencies.Input(component_id='input_value', component_property='value')])
def update_file_value(value_to_write):
print('set value!')
print('value_to_write: ' + str(value_to_write))
f = open('./value.txt', 'w')
f.write(str(value_to_write))
f.close()
return value_to_write
if __name__ == '__main__':
app.run_server(debug=True)
I walked through my process step by step as well:
| step | description | printed | cat | gui | |
| | | | | box | output |
|------+---------------------------------+--------------------------------------+-------+-------+--------|
| 0 | touch value.txt | - | - | - | - |
| 1 | echo "1" > value.txt | - | 1 | - | - |
| 2 | python debug-get-set.py | getting value! current_value: 1 (2x) | 1 | - | - |
| 3 | open localhost:8050 | set value! value_to_write: 1 | 1 | 1 | 1 |
| 4 | click in box; delete 1 | set value! written value: empty | empty | blank | blank |
| 5 | type 2 in box | set value! written value: 2 | 2 | 2 | 2 |
| 6 | click to remove cursor from box | no message | 2 | 2 | 2 |
| 6 | Ctrl+R; reload page | set value! value_to_write: 1 | 1 | 1 | 1 |
This seems odd to me, but I’m very new to dash, so please let me know where my thinking is awry. Throughout the changes, we can check four key values:
- the value read from the file, stored as
current_value - the
dcc.Inputdefault value, passed asvalue=current_value, to be shown as the prompt in the text input box - the value passed to the callback and written to the file,
value_to_write, which we call if ourdcc.Inputvalue changes (not sure howdashchecks for a change) - the return from the callback, put on the page with
html.Div(id='file_value_written')
Upon load, they all agree on the value being 1. It’s printed, the file contains it, and it’s displayed in the box and below the box. We change the box contents to 2, and the effect pushes through to the html.Div on the screen and the file (checked with cat). It’s also still in the box, obviously.
When we reload we’re told we’re setting the file contents, but that should only change if we’re in callback. That only happens if the current text box changes, and we didn’t touch it. It almost behaves like the value currently in the box might be cached (or in some sense not really changed), and even when we delete/replace it with 2, the cached/stored value is 1. On refresh, things work in reverse: 2 is treated as the last value seen and the cached value, 1, shows up as a user change and is written. Is that a crazy interpretation?
What I expected: reloading would re-run things top to bottom, it should read 2 from the file, use 2 as the default value in the box, and wait for a callback trigger if the user changes what’s in the box 2.
This was a wild chase to just work through in my head and I am coming up clueless. Thanks for taking a look!
Issue Analytics
- State:
- Created 6 years ago
- Comments:6 (3 by maintainers)

Top Related StackOverflow Question
Nope, this is not odd. In the code you posted, you’re reading the value of the file within the
serve_layoutfunction, which is only ever evaluated once – the first time the app is loaded. So every time you hit refresh, you’re getting the same layout object that was previously evaluated and assigned toapp.layout, irrespective of the contents of that file.You raise an interesting point though, when you assign
app.layoutto the actual functionserve_layout, as opposed to evaluating it and assigningapp.layoutto the component object it returns, this behaviour changes to display the current results of the file. It would seem that when you do this, Dash does indeed evaluateapp.layouton every page load.So which should you use? I think it just depends on what you’re trying to do. Personally, I think it’s better practice to default to putting all the dynamic generation of things within your callbacks (so in your case you would read the value of your file only within callbacks). This way you’re less likely to run into surprises like this. Also, using a static
app.layout(as opposed function that’s evaluated on every page load) also means that any run-time errors in the evaluation of your layout will happen immediately on initialisation of your app rather than later on while someone is using it. But there may be times we you really do need dynamic evaluation ofapp.layout(maybe you have to call a database or something) so then you’d use a function.Thanks for the tips @ned2 . I didn’t initially expect the callbacks to run all the time, but now that @chriddyp clarified how those work (run on every load/refresh), having anything dynamic happen in them is a great point I hadn’t considered. Thanks to both of you for enduring a noob 😃