dash icon indicating copy to clipboard operation
dash copied to clipboard

persistence is differently handled for `dcc.Dropdown` than for the other dcc components

Open petar-qb opened this issue 4 months ago • 0 comments

Hi Dash wizards 👋

I've been using Dash extensively for several years, and this is the first time I've encountered an issue that might be considered a bug.

Dependencies:

dash                       2.18.1
dash_ag_grid               31.2.0
dash-bootstrap-components  1.6.0
dash-core-components       2.0.0
dash-html-components       2.0.0

Describe the bug

It appears that the session persistence storage is adjusted for the dcc.Dropdown when the object is completely recreated with new "options". This does not happen for the dcc.Checklist, dcc.RadioItems. It also does not happen for the dcc.Slider and dcc.RangeSlider when min and max are changed.

I'm unsure whether this is intended behaviour or if it's a bug. If it is a bug, it’s unclear whether it lies in dcc.Dropdown or the other dcc components I mentioned above.

Here's a small example:

from dash import Dash, html, dcc, Output, callback, Input, exceptions


app = Dash(__name__)

app.layout = html.Div([
    html.H1("Hello Dash"),
    html.Button(
        id="button_id",
        children="Recreate components below",
    ),
    html.Div(
        id="components_div_id",
        children=[
            dcc.Dropdown(
                id="dropdown_id",
                options=["A", "B", "C"],
                value=["A", "B", "C"],
                multi=True,
                persistence=True,
                persistence_type="session",
            ),
            dcc.Checklist(
                id="checklist_id",
                options=["A", "B", "C"],
                value=["A", "B", "C"],
                persistence=True,
                persistence_type="session",
            ),
        ]
    )
])


@callback(
    Output("components_div_id", "children"),
    Input("button_id", "n_clicks"),
    prevent_initial_call=True
)
def recreate_component(n_clicks):
    print(n_clicks)
    return [
        dcc.Dropdown(
            id="dropdown_id",
            # TODO - Important: "options" property is changed.
            options=["A", "B"],
            value=["A", "B", "C"],
            multi=True,
            persistence=True,
            persistence_type="session",
        ),
        dcc.Checklist(
            id="checklist_id",
            # TODO - Important: "options" property is changed.
            options=["A", "B"],
            value=["A", "B", "C"],
            persistence=True,
            persistence_type="session",
        ),
    ]


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

Expected behavior

I would expect the persistence storage for components like dcc.Checklist, as demonstrated in the example, to also adjust when recreated with new "options", similar to how dcc.Dropdown behaves.

In my opinion, dcc.Dropdown behaves more appropriately because the persisted values match the currently selected values that users see in the UI.

Screenshots

dash_dcc_persistence_bug

P.S. I've noticed that the recordUiEdit function in src/persistence.js is triggered for dcc.Dropdown, but not for dcc.Checklist and the other components mentioned. Could this be due to the asynchronous handling specific to dcc.Dropdown?

petar-qb avatar Oct 16 '24 05:10 petar-qb