Store component breaks with nested data. #332
Description
The Store component should support serialization and deserialization of standard python variable types (JSON.stringify() and JSON.parse()). However, the component does not properly validate the data if it is a dictionary with keys comprising of either lists or additional dicts:
return dict(mykey=[])
return dict(mykey = dict())
The data is saved properly to session/local storage (I did not try memory), but fails after componentUpdate. I didn't dig into it, but believe this is a result of how the dataCheck function evaluates this type of data structure against what is already stored (always returns true, data has changed). End result is that modified_timestamp callback is triggered in an infinite loop.
function dataCheck(data, old) { ... }
I'm currently working around this issue by serializing the information myself rather than relying on the built-in handler.
return json.dumps(dict(mykey=[]))
See below for a demo. Issue applies to empty/non-empty lists/dicts.
import json
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Store(
id='store',
storage_type='local'
),
html.Button(
id='btn1',
children='break me'
),
html.Button(
id='btn2',
children='pre-serialize (safe)'
),
html.Div(id='output')
])
@app.callback(
Output('store', 'data'),
[Input('btn1', 'n_clicks'),
Input('btn2', 'n_clicks')],
[State('btn1', 'n_clicks_timestamp'),
State('btn2', 'n_clicks_timestamp')]
)
def unsafe_click(n_clicks1, n_clicks2, tm_clicks1, tm_clicks2):
if n_clicks1 is None and n_clicks2 is None:
raise dash.exceptions.PreventUpdate
data = dict(key=[]) # dict(key=dict())
tm_clicks1 = tm_clicks1 or -1
tm_clicks2 = tm_clicks2 or -1
if tm_clicks2 > tm_clicks1:
data = json.dumps(data)
return data
@app.callback(
Output('output', 'children'),
[Input('store', 'modified_timestamp')],
[State('store', 'data')]
)
def add_to_storage(timestamp, data):
if timestamp is None:
raise dash.exceptions.PreventUpdate
print('current storage is {}'.format(data))
print('modified_timestamp is {}'.format(timestamp))
return json.dumps(data)
if __name__ == '__main__':
app.run_server(debug=True, threaded=True, port=3033)