Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable storing the callback outputs in the persistence storage #3144

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from

Conversation

petar-qb
Copy link

This PR:

TLDR:
This PR adds a callback's property enable_persistence: bool (by default False). If it's True, callback output value will be written within the browser's persistence storage for its dcc components that has persistence=True set.


For the following code (also posted in the Dash Plotly forum question that's linked above):

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

app = Dash(__name__)

app.layout = html.Div([
    html.Button("Select All", id="select_all_button"),
    dcc.Checklist(
        id='checkbox',
        options=[1, 2, 3],
        value=[],
        persistence=True,
        persistence_type='session'
    ),
])


@app.callback(
    Output('checkbox', 'value'),
    Input('select_all_button', 'n_clicks'),
    prevent_initial_call=True,
    # Uncomment the following configuration:
    # enable_persistence=True
)
def select_all_options(n_clicks):
    return [1, 2, 3]


if __name__ == '__main__':
    app.run_server(debug=True)

There are screen recordings of how it works from the plotly/dash::dev branch vs the petar-qb/dash::feature/callback_enable_persistence branch:

How it works from the plotly/dash::dev (it works exactly the same from the feature branch too if the callback enable_persistence is not set or is set to False):

Screen.Recording.2025-01-30.at.11.07.24.mov

How it works from the petar-qb/dash::feature/callback_enable_persistence when enable_persistence=True is added :

Screen.Recording.2025-01-30.at.11.09.25.mov

There's a sketch that represents the new behaviour:
image

The only difference between the current Dash behaviour and the feature branch is the third case in the sketch above. Specifically, when enable_persistence=True is set in the callback, the new value returned from the server will be stored persistently instead of being pruned.


Contributor Checklist

  • I have broken down my PR scope into the following TODO tasks

    • Add enable_persistence into the callback and clientside_callback signature (by default it's False).
    • Call recordUiEdit even on callback response.
    • Within executedCallback:
      • Do not prune persistence if enable_persistence=True,
      • Do not apply persistence if enable_persistence=True,
      • Call updateProps that will call recordUiEdit only if enable_persistence=True
    • Enable that the recordUiEdit can recursively record edits for children. This enables that setting the persistence from the callback output works even if nested object is returned.
  • I have run the tests locally and they passed. (refer to testing section in contributing)

  • I have added tests, or extended existing tests, to cover any new features or bugs fixed in this PR

optionals

  • I have added entry in the CHANGELOG.md
  • If this PR needs a follow-up in dash docs, community thread, I have mentioned the relevant URLS as follows
    • this GitHub #PR number updates the dash docs
    • here is the show and tell thread in Plotly Dash community

@petar-qb
Copy link
Author

Every time I make changes to the dash-rendered code locally, I have to run renderer build local before testing.
Is there a simpler or more efficient way to do this?

@petar-qb
Copy link
Author

Is "enable_persistence" the best name for this? Would something like "set_persistence" be a better fit? What do you think?

@petar-qb
Copy link
Author

I see this more as a bug fix rather than a new feature. If you agree, we should then default the enable_persistence to True and explicitly set it to False for the callbacks like:

dash.py -> LN: 2237

            @self.callback(
                Output(_ID_CONTENT, "children"),
                Output(_ID_STORE, "data"),
                inputs=inputs,
                prevent_initial_call=True,
                enable_persistence=False
            )
            def update(pathname_, search_, **states):

What are your thoughts?

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