plotly / dash

Data Apps & Dashboards for Python. No JavaScript Required.
https://plotly.com/dash
MIT License
21.32k stars 2.06k forks source link

dcc.Store callback firing multiple times #1153

Open devmcp opened 4 years ago

devmcp commented 4 years ago

Describe your context

dash                      1.9.1               
dash-bootstrap-components 0.8.3               
dash-core-components      1.8.1               
dash-daq                  0.4.0               
dash-html-components      1.0.2               
dash-renderer             1.2.4               
dash-table                4.6.1               
- OS: MacOS 10.15.3
- Browser: Safari 
- Version 13.0.5

Describe the bug

The dcc.Storage component seems to trigger twice (sometimes more) on starting the app and sometimes on subsequent triggers. See example below.

Minimal working example code:

import dash

import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Output, Input, State
from dash.exceptions import PreventUpdate

app = dash.Dash(__name__)

app.layout = html.Div([
    dcc.Store(id='local', storage_type='local'),
    html.Div(html.Button('localStorage', id='local-button')),
    html.Div(0, id='local-clicks'),
])

@app.callback(Output('local', 'data'),
              [Input('local-button', 'n_clicks')],
              [State('local', 'data')])
def on_click(n_clicks, data):
    if n_clicks is None:
        raise PreventUpdate

    app.logger.info(f"Updating data store")

    data = data or {'clicks': 0}

    data['clicks'] = data['clicks'] + 1
    return data

@app.callback(Output('local-clicks', 'children'),
              [Input('local', 'modified_timestamp')],
              [State('local', 'data')]
              )
def on_data(ts, data):
    if ts is None:
        raise PreventUpdate

    app.logger.info(f"New data found! ({ts}, {data})")

    return f"{ts}, {data['clicks']}"

if __name__ == '__main__':
    app.run_server(debug=True, port=8077, threaded=True)

Example output:

Running on http://127.0.0.1:8077/
Debugger PIN: 597-637-135
New data found! (1584011957193, {'clicks': 24})
New data found! (1584011957193, {'clicks': 24})
Updating data store
New data found! (1584012443177, {'clicks': 25})
New data found! (1584012443177, {'clicks': 25})
Updating data store
New data found! (1584012445159, {'clicks': 26})
New data found! (1584012445159, {'clicks': 26})

Expected behavior

The callback should fire once.

Karan-S-Mittal commented 2 years ago

I also faced the same issue with my dcc.store(). I update the store in a client-side call and then call the server callback. here's a version inspired by your example.

Version Informations

dash                      2.4.1                    pypi_0    pypi
dash-core-components      2.0.0                    pypi_0    pypi
dash-html-components      2.0.0                    pypi_0    pypi
dash-daq                  0.5.0                    pypi_0    pypi

Reproduce the Bug.

import datetime
import dash
from dash import html, dcc
from dash.dependencies import Output, Input, State
from dash.exceptions import PreventUpdate

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        dcc.Store(id="local", storage_type="local"),
        html.Div(html.Button("localStorage", id="local-button")),
        html.Div(0, id="local-clicks"),
    ]
)

app.clientside_callback(
    """
    function(n_clicks, data) {
        if (n_clicks) {
            console.log("Updating data store");
            data = data || {clicks: 0};
            data.clicks = data.clicks + 1;
            return data;
        }
    }
    """,
    Output("local", "data"),
    [Input("local-button", "n_clicks")],
    [State("local", "data")],
)

@app.callback(
    Output("local-clicks", "children"),
    [Input("local", "modified_timestamp")],
    [State("local", "data")],
)
def on_data(ts, data):
    print(datetime.datetime.now())

    print(f"New data found! ({ts}, {data})")
    print("-" * 20)
    return f"{ts} - {data['clicks']}"

if __name__ == "__main__":
    app.run_server(debug=True, port=8077)

Expected Output

2022-06-25 15:22:20.186572
New data found! (1656150740170, {'clicks': 85})
--------------------

Current Output

Screenshot 2022-06-25 at 3 22 25 PM

and in the browser console

Screenshot 2022-06-25 at 3 22 33 PM
Karan-S-Mittal commented 2 years ago

Please help, is someplace I can look at if to resolve this.