Unregistered dcc input components are having their contents cleared after callback updates
ned2 opened this issue · 14 comments
I haven't tested this thoroughly, but looks like it might be the case that when a dcc input component is included in a layout but is not registered with a callback, when any callback runs and the page is updated, the unregistered component's input contents are cleared. eg a dropdown that had a user selection made is reset to its initial value. If the component is then registered as a State of a callback whose value is is ignored, the initial value is then preserved as expected after a callback updates.
See this discussion: https://community.plot.ly/t/callbacks-clearing-all-unconnected-core-components-values/7631
While having unused components is probably an unlikely scenario in a completed app, it's certainly going to be confusing behaviour if someone has created a complete layout and is then going through implementing and testing callbacks incrementally -- as occurred in that discussion.
(hopefully I landed this in the right repo!)
Hi thanks for posting the issue and linking back to the community post. The script posted in the community forums is fairly lengthy. Are you able to post a minimal Dash script here that can reproduce the issue?
If you can make that happen we'll reproduce the issue on our end and take action. Thanks!
Finally got around to making a minimal example that illustrates this buggy behaviour.
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, State, Output
app = dash.Dash()
app.layout = html.Div([
html.Div(id='target'),
dcc.Input(id='my-input', type='text', value=''),
html.Br(),
dcc.Input(id='my-input-missing', type='text', value=''),
html.Br(),
html.Button(id='submit', n_clicks=0, children='Save')
])
@app.callback(Output('target', 'children'), [Input('submit', 'n_clicks')],
[State('my-input', 'value')])
def callback(n_clicks, state):
return "callback received value: {}".format(state)
if __name__ == '__main__':
app.run_server(debug=True)
The second Input component in the layout is not registered with a callback. If you enter text into both inputs then hit save, the second one's text will be cleared. Perhaps we should be catching this situation in layout validation since this is not a situation that a user probably meant to arrive at, with an unused component. However it would still be good to stop the clearing of the field anyway.
Hey @bpostlethwaite, I've put together a minimal example that shows that this indeed a bug. What do you think the next steps here are?
We'll queue it for fixing! Thanks for posting the minimal example
It's the dcc.Input that doesn't have a check if the props is the same as the initial value before setting setState in componentWillReceiveProps. I added one and the bug was gone.
Due to how the dash-renderer render the layout, the components will receive the same initial props when updating the layout so they should check that they are not given the same initial props when using state and they don't have setProps.
@ned2 I fixed the Input in plotly/dash-core-components#350, I think that is something we'll have to watch out for when developing dash component as I don't think we can patch that in the renderer.
Great! Ah, so it was specific to dcc.Input. We should update the relevant gotcha entry in the FAQs page then. Maybe just remove it entirely from that page, and then potentially also add something to the Component Development section, as it sounds like the gotcha now applies to component developers rather than users?
Or is it worth leaving it in as a potential gotcha in the event that a component author hasn't handled this?
It's not specific to the input, the fix I had made was actually incomplete, the good pattern is the one @valentijnnieman did in plotly/dash-core-components#356
Only use the state if there's no setProps, don't derive state from the props or they might get the props from the initial layout.
I think it's because of the initial layout and hydration of the components in TreeContainer, they get the props back when a component update because the TreeContainer updates. I would leave this issue because it has some context and someone might be able to find a fix for that in the future.
If you don't use an Input's value in any callback, it will be cleared.
It's a bug, but not terribly important, as if you don't care about that Input, Dash not caring either isn't the end of the world
Confused me for a while too
@ned2 With #126, the renderer's behavior has changed. setProps
is now always present and it will always update the component props, whether it is used in a callback or not. If it is used in a callback, the props will be filtered and only the necessary props will be sent to Dash for processing. To be released in Dash 0.40.0.
While having unused components is probably an unlikely scenario in a completed app
Very late to the party, but I think it's worth pointing out that with the table, it is frequent for that component to be the last in a chain of callbacks and for no one but the end-user to be making changes to it (e.g. readonly w/ front-end driven filtering/sorting), resulting in an undefined setProps and weird behavior.
That's amazing! I've been seeing this issue pop up a fair bit in the forums recently. Definitely great for a smoother Dash development experience.
Actually, I just tried out the example I posted a while back, and that buggy behaviour with the input seems to be already fixed in a previous release.
I might try to test it with a couple of other dcc types before closing the issue. I think I recall seeing this happening with dcc.Dropdown also.
Just tested this with a missing Dropdown element, and its also not being cleared. Hooray!
Strangely, as mentioned, this is working on the current version of Dash, so a previous change seems to have improved the situation already. Regardless, I think we can close this now.