# Infinite loop with dcc.Store?

**URL:** <https://community.plotly.com/t/infinite-loop-with-dcc-store/16632>\
**Category:** Dash Python\
**Created:** [December 7, 2018, 12:01am UTC](https://community.plotly.com/t/infinite-loop-with-dcc-store/16632 "2018-12-07T00:01:20Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![kolaworld](https://avatars.discourse-cdn.com/v4/letter/k/35a633/32.png) [@kolaworld](https://community.plotly.com/u/kolaworld)\
**Post date:** [December 7, 2018, 12:01am UTC](https://community.plotly.com/t/infinite-loop-with-dcc-store/16632/1 "2018-12-07T00:01:20Z")

</div>

I discovered what appears to be a bug after trying and failing to adapt the “Generic Crossfilter Recipe” found here [https://dash.plot.ly/interactive-graphing](https://dash.plot.ly/interactive-graphing) such that the x and y axes of the scatter plots can be set dynamically with dcc.Dropdown components.

Here is the code to reproduce the infinite loop caused by dcc.Store’s modified\_timestamp property being used as an input in callbacks to update my scatter plots. **Warning, running the below will cause an infinite loop so please be sure to stop the run and exit your tab/browser after running:**

```auto
import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
import pandas as pd
from dash.dependencies import Input, Output, State

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash( __name__ , external_stylesheets=external_stylesheets)

np.random.seed(0)
df = pd.DataFrame({
    'Column {}'.format(i): np.random.rand(30) + i*10
    for i in range(6)})

app.layout = html.Div([
    dcc.Store(id='data_store', storage_type='session'),

    # html.Div(
    # dcc.Dropdown(id='g1x', options=[{'label': i, 'value': i} for i in df.columns], value=df.columns[0])
    # ),
    # html.Div(
    # dcc.Dropdown(id='g1y', options=[{'label': i, 'value': i} for i in df.columns], value=df.columns[1])
    # ),
    # html.Div(
    # dcc.Dropdown(id='g2x', options=[{'label': i, 'value': i} for i in df.columns], value=df.columns[2])
    # ),
    # html.Div(
    # dcc.Dropdown(id='g2y', options=[{'label': i, 'value': i} for i in df.columns], value=df.columns[3])
    # ),
    # html.Div(
    # dcc.Dropdown(id='g3x', options=[{'label': i, 'value': i} for i in df.columns], value=df.columns[4])
    # ),
    # html.Div(
    # dcc.Dropdown(id='g3y', options=[{'label': i, 'value': i} for i in df.columns], value=df.columns[5])
    # ),
    html.Div(
        dcc.Graph(
            id='g1',
            config={'displayModeBar': False}
        ), className='four columns'
    ),
    html.Div(
        dcc.Graph(
            id='g2',
            config={'displayModeBar': False}
        ), className='four columns'),
    html.Div(
        dcc.Graph(
            id='g3',
            config={'displayModeBar': False}
        ), className='four columns'),

], className='row')

@app.callback(Output('data_store', 'data'),
              [Input('g1', 'selectedData'), Input('g2', 'selectedData'), Input('g3', 'selectedData')],
              [State('data_store', 'data')])
def update_data_store(g1_sel_data, g2_sel_data, g3_sel_data, prev_data):
    print('update_data_store - A')
    if not g1_sel_data and not g2_sel_data and not g3_sel_data:
        return
    print('update_data_store - B')
    data = prev_data if prev_data else {'g1': None, 'g2': None, 'g3': None, 'sel_data': None}

    if data['g1'] is not g1_sel_data:
        data['sel_data'] = g1_sel_data
        data['g1'] = g1_sel_data

    if data['g2'] is not g2_sel_data:
        data['sel_data'] = g2_sel_data
        data['g2'] = g2_sel_data

    if data['g3'] is not g3_sel_data:
        data['sel_data'] = g3_sel_data
        data['g3'] = g3_sel_data

    print('update_data_store - C', data)

    return data

@app.callback(Output('g1', 'figure'),
              [#Input('g1x', 'value'), Input('g1y', 'value'),
               Input('data_store', 'modified_timestamp')], [State('data_store', 'data')])
def update_figure_1(ts, stored_data):
    print('UPDATE_FIGURE_1 - A', ts)
    if not ts: raise dash.exceptions.PreventUpdate
    print('UPDATE_FIGURE_1 - B', stored_data)
    x = 'Column 0'
    y = 'Column 1'

    selected_data = stored_data['sel_data'] if stored_data else None
    selected_points = get_selected_points(df.index, selected_data)
    return get_figure(x, y, selected_points, selected_data, 'g1', stored_data)

@app.callback(Output('g2', 'figure'),
              [#Input('g2x', 'value'), Input('g2y', 'value'),
               Input('data_store', 'modified_timestamp')], [State('data_store', 'data')])
def update_figure_2(ts, stored_data):
    if not ts: raise dash.exceptions.PreventUpdate
    x = 'Column 2'
    y = 'Column 3'

    selected_data = stored_data['sel_data'] if stored_data else None
    selected_points = get_selected_points(df.index, selected_data)
    return get_figure(x, y, selected_points, selected_data, 'g2', stored_data)

@app.callback(Output('g3', 'figure'),
              [#Input('g3x', 'value'), Input('g3y', 'value')])
               Input('data_store', 'modified_timestamp')], [State('data_store', 'data')])
def update_figure_3(ts, stored_data):
    if not ts: raise dash.exceptions.PreventUpdate
    x = 'Column 4'
    y = 'Column 5'

    selected_data = stored_data['sel_data'] if stored_data else None
    selected_points = get_selected_points(df.index, selected_data)
    return get_figure(x, y, selected_points, selected_data, 'g3', stored_data)

def get_selected_points(df_index, selected_data):
    selected_points = df_index
    if selected_data is not None:
        selected_index = [
            p['customdata'] for p in selected_data['points']
        ]
        if len(selected_index) > 0:
            selected_points = np.intersect1d(selected_points, selected_index)

    return selected_points

def get_figure(x, y, selected_points, selected_data, graph_id, stored_data):
    figure = {
        'data': [
            {
                'x': df[x],
                'y': df[y],
                'text': df.index,
                'textposition': 'top',
                'selectedpoints': selected_points,
                'customdata': df.index,
                'type': 'scatter',
                'mode': 'markers+text',
                'marker': {
                    'color': 'rgba(0, 116, 217, 0.7)',
                    'size': 12,
                    'line': {
                        'color': 'rgb(0, 116, 217)',
                        'width': 0.5
                    }
                },
                'textfont': {
                    'color': 'rgba(30, 30, 30, 1)'
                },
                'unselected': {
                    'marker': {
                        'opacity': 0.3,
                    },
                    'textfont': {
                        # make text transparent when not selected
                        'color': 'rgba(0, 0, 0, 0)'
                    }
                }
            },
        ],
        'layout': {
            'margin': {'l': 15, 'r': 0, 'b': 15, 't': 5},
            'dragmode': 'select',
            'hovermode': 'closest',
            'showlegend': False
        }
    }

    # Display a rectangle to highlight the previously selected region
    shape = {
        'type': 'rect',
        'line': {
            'width': 1,
            'dash': 'dot',
            'color': 'darkgrey'
        }
    }
    if selected_data and selected_data is stored_data[graph_id] and selected_data['range']:
        figure['layout']['shapes'] = [dict({
            'x0': selected_data['range']['x'][0],
            'x1': selected_data['range']['x'][1],
            'y0': selected_data['range']['y'][0],
            'y1': selected_data['range']['y'][1]
        }, **shape)]
    else:
        figure['layout']['shapes'] = [dict({
            'type': 'rect',
            'x0': np.min(df[x]),
            'x1': np.max(df[x]),
            'y0': np.min(df[y]),
            'y1': np.max(df[y])
        }, **shape)]

    return figure

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

```

Besides the infinite loops, I’ve also noticed that the ‘selectedData’ property of dcc.Graph does not always trigger a callback when passed as an input. Additionally, the ‘data’ property of dcc.Store also doesn’t trigger call backs when passed as an input.
