4
votes

I'm having an issue with Plotly Dash whereby the callback that is triggered by a dcc.Store component is firing twice every time. See the code and example output code below, which is based on the example in the Dash docs (https://dash.plot.ly/dash-core-components/store).

Can anyone explain this or suggest a workaround to prevent it?

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})
1
Weird. My first thought is to remove the threading command from the line app.run_server -- perhaps this is causing duplicate logging.Yaakov Bressler
Thanks. Tried that and it doesn't help. Are you able to reproduce the issue?Mad
I don't get duplicate logs... Weird. Could be how the data is being stored in the dcc object? What if you changed from local to session – ?Yaakov Bressler
What version of Dash are you using? The callback logic was changed significantly in version 1.11.0, so updating to this version might solve your issue.emher
@blong Thanks for your kind suggestion. I was doing some more testing to confirm. Unfortunately, the bug did not occur with the original code, i.e. with threaded=True kept (tested for dash==1.18.1, 1.17.0, and 1.11.0) Let's continue figure out what happenendClaire

1 Answers

0
votes

I'd suggest converting the memory store to session and changing the code in the second callback to the following:

def on_data(ts, data):

    if not data or not ts:
        raise PreventUpdate

    ...

That should resolve some of the first few circular callbacks.