Skip to content

Conversation

@AnnMarieW
Copy link
Collaborator

Potential fix for #3330

Note -this solution was provided by a contributor who is unable to submit a PR.

In sample dash app below, a callback updates the children component with a layout of nested components. If there is a change in one of the nested components there can be unexpected results.

For example, if you select a date in the date picker, then "refresh" the layout in a callback, it re-renders the old state when you interact with the date picker again. This error happens in Dash 3 but not in Dash 2.

A workaround is to give the layout's parent component an id that changes with each update. This forces the component to re-render correctly.

This PR makes that workaround unnecessary. It updates the reducer in the dash renderer so it clears out the child path hashes and prevents stale values from triggering incorrect renders

Potential issues:

  • This may impact performance when the children component has many nested components
  • May cause issues when using partial property updates.

import dash
from dash import Input, Output, State, dcc, html

app = dash.Dash(__name__, prevent_initial_callbacks=True)


def layout(refresh_count=0):
    return html.Div(
       # id=f"container-{refresh_count}",  # this works
        id="container",                    # this doesn't work
        children=[
            html.H1(f"Layout refreshed {refresh_count} times"),
            html.P(
                html.Ul(
                    [
                        html.Li("Pick a date in the date picker"),
                        html.Li("Click on the 'Refresh layout' button"),
                        html.Li(
                            "Open date picker. It will change its value and text on the right will change too"
                        ),
                    ]
                )
            ),
            html.Div(
                html.Div(
                    html.Div(
                        id="nested-container",
                        children=[
                            dcc.DatePickerSingle(id="date-picker", date=None),
                            html.Span(
                                f"Nothing picked, {refresh_count} refreshes",
                                id="text",
                                style={
                                    "marginLeft": "10px",
                                    "backgroundColor": "#d0d0d0",
                                    "padding": "5px",
                                },
                            ),
                        ],
                    ),
                ),
            ),
        ],
    )


app.layout = html.Div(
    [
        html.H1("Dash bug showcase"),
        html.Button("Refresh layout", id="refresh-button"),
        html.P(
            id="layout",
            style={"padding": "20px", "border": "2px dashed red"},
            children=layout(),
        ),
    ]
)


@app.callback(
    Output("layout", "children"),
    Input("refresh-button", "n_clicks"),
)
def update_output(n_clicks):
    return layout(n_clicks)


@app.callback(
    Output("text", "children"),
    Input("date-picker", "date"),
    State("refresh-button", "n_clicks"),
)
def update_text(date, n_clicks):
    return f"{date}, {n_clicks} refreshes"


if __name__ == "__main__":
    app.run(debug=True)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant