dash icon indicating copy to clipboard operation
dash copied to clipboard

Fix incorrect rendering in nested children

Open AnnMarieW opened this issue 1 month ago • 2 comments

closes #3330

Note -this solution was provided by a Dash Enterprise customer. :tada:

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)


AnnMarieW avatar Nov 07 '25 23:11 AnnMarieW

@T4rk1n @AnnMarieW I see performance risks:

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

Can we mitigate these issues or does this need to be an optional rendering config?

ndrezn avatar Dec 02 '25 17:12 ndrezn

Here's an interesting bit of data. The example above works fine in Dash 4 ( using the new dcc date picker), but does not work in Dash 3.

One more clue: In Dash 3, if the children are wrapped in a div instead of just being a list, it updates correctly:

                    children=html.Div([     # This line changed
                            dcc.DatePickerSingle(id="date-picker", date=None),
                            html.Span(
                                f"Nothing picked, {refresh_count} refreshes",
                                id="text",
                                style={
                                    "marginLeft": "10px",
                                    "backgroundColor": "#d0d0d0",
                                    "padding": "5px",
                                },
                            ),
                        ]),

AnnMarieW avatar Dec 02 '25 17:12 AnnMarieW