App not resetting with page refresh

Here I am adding a new item to a checklist, based on a radiobutton selection. Problem is, if we agree that it is a bug and not a feature, is that even if I refresh the page, the app does not get set to the initial state. In the gif below you can see that the options ‘1’ and ‘2’ in the checklist, once added via radio button, persist even after a page refresh.

In the callback I am doing digging around in the layout to compare the current options to the new one:

@app.callback(
    dash.dependencies.Output('cities-checklist', 'options'),
    [dash.dependencies.Input('radio', 'value')])
def update_list(value):
    options = app.layout['cities-checklist'].options
    currValues = [x['value'] for x in options]
    if value not in currValues and value:  # 'value' may be empty, and we don't want to append an empty item to checklist!
        options.append({'label':value, 'value':value})
    return options

Of course I could add another button that resets to defaults on click, but… Is there a better way to do this?

Post your layout. Can’t say anything from just the callback.

Woah, that’s pretty weird. I haven’t seen that before. Agreed that it looks like a bug :+1: :slight_smile:

Could you share the entire (reproducable) example? There might be something else going on here.

Ready for copy-paste!

import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()

''' The layout'''
app.layout = html.Div(
    [
    dcc.Markdown("First select a RadioItem, and it should be added to the Checklist. Then hit referesh to see if it persists!!/ "),
    dcc.RadioItems(id='radio',options=[{'label':'1','value':'1'},{'label':'2','value':'2'}],value=[]
    ),
    dcc.Checklist(id='cities-checklist',
        options=[
            {'label': 'New York City', 'value': 'NYC'},
            {'label': 'Montréal', 'value': 'MTL'},
            {'label': 'San Francisco', 'value': 'SF'}],
        values=['MTL', 'SF'],
        labelStyle={'display':'inline-block'}
    ),
    html.Div(id='hidden', style={'display': 'none'})
    ]
)

''' Sink the checklist events into the hidden div'''
@app.callback(
    dash.dependencies.Output('hidden', 'children'),
    [dash.dependencies.Input('cities-checklist', 'values')])
def update_hidden_div(*args):
    return ''

''' Update the Checklist '''
@app.callback(
    dash.dependencies.Output('cities-checklist', 'options'),
    [dash.dependencies.Input('radio', 'value')])
def update_list(value):
    options = app.layout['cities-checklist'].options
    currValues = [x['value'] for x in options]
    if value not in currValues and value:
        options.append({'label':value, 'value':value})
    return options

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

You are appending the new selected radio button value to options dictionary which is a global variable here. That is why, once you select a radio button, it adds to options list & stays there even when you reload the page.

You have to somehow find a way to reset the options to original list on page reload (or use a button to reset?)

Hi Raghunath. Yes, in the callback I am appending to the options property of the checklist:

if value not in currValues and value:  # 'value' may be empty, and we don't want to append an empty item to checklist!
    options.append({'label':value, 'value':value})

So, would you call this persistent, global variable a bug or a feature? It would be nice to have a way to reset the page, right? On the other hand, it may be that the Dash team has a different pattern, or usage, in mind.

I am wondering if we should be using classes, with dash.Dash instances as class members?

Or roll our own frameworks, and insert Dash plots as we see fit? If this is the case, I would hate to re-write a framework after people have already put this much effort into it. But there also seems to be a path toward this solution, via javascript plugins.

Or as you said, have a way to reset everything to a clean state. This could be a good option.

I am wondering what other people have done to solve this.

One of the key principles of Dash is that variables should not be mutated out of the scope of callbacks. In this case you’re mutating app.layout['cities-checklist'].options inside the callback - this “isn’t allowed”.

Mutable state in the python session is dangerous because one user’s session can end up modifying the next user’s session. Also, when apps are deployed, their environment contains multiple processes (e.g. with gunicorn) and the mutations from one python session won’t persist or carry across to the next python session.

@alex_01 What effect are you trying to have here? If you want to have similar logic but have the app “reset” on page reload, then you can do something really similar by making a copy of the options instead of mutating them. That is:

import copy

....

def update_list(value):
    options = copy.copy(app.layout['cities-checklist'].options)
    ...
1 Like

Okay, good. Now we’re getting somewhere. I agree, what I was doing is hacky. And I don’t want to be digging into the stack where users have no business poking around. I hear your point about mutating variables outside of callbacks. Though I am new to web development, I think that Flask has something similar going on with the app context, except that they throw an exception when you try to do something outside of context.

But on the other hand, copy.copy isn’t going to work, because I do need each checklist item to persist. I could make another singleton object whose object is to watch over that

Below you can see the effect: I can add the “1” checkbox or the “2” checkbox, but not both:

http://recordit.co/qRtMOGTvIg

So, you’re probably wondering why I want to do this. And it may be that there is already an example in the user guide. I’ll go back and double-check the tutorials.

In the meantime, imagine having hundreds, or thousands of datasets, and you can’t load all of them. But you have a “search function” that returns certain datasets based on some criteria. You then select a subset of the list returned by the “search function”, and add those to a list of candidate datasets.

You then perform another search, or several searches, choosing datasets to add to your list of candidate datasets (and store them in a CheckList).

Once you are satisfied with your list, you can select a subset from the list of candidate datasets, and then plot only those datasets. (You do this by selecting from the CheckList.)

This is essentially “slicing” by year, based on the sliders, in the examples. Except that “years” are sequential, and can be indexed with a slider. (also, in this example we would be slicing twice)

There is probably a better way of doing this… Plotly comes with one out of the box: When you have multiple traces within a single plot, you get a legend, and you can choose which traces to turn on or off.

But what happens when you have 10 plots, and each one has 2 traces each. Do you really want to scroll through all 10 and turn off one trace in each plot?

If this is too complicated, I understand that it is a sign that I need to step back and think more about the architecture…

What do you mean by persist here? This could mean a few things:

  • When the user refreshes the page, the selections should stay the same
  • If a different user visists the page, they see the globally selected selection
  • As the user clicks around, previously selected values don’t change

I suspect that it’s the 3rd one, but I just want to make sure :slight_smile:

Let’s go back animated gif at the top of this post. When the user selects 1, and then 2 from the radio buttons, both 1 and 2 remain appended to the Checklist items. This is good behavior. But bad implementation (as you pointed out above, I am accessing the app layout without going through the callback mechanism directly – I still need advice on that one and may create another post).

The question still remains whether there is a way to reset everything to an initial state.

User @raghunath also requested this a few days back: Reset to Initial state without reloading page

As the user clicks around, previously selected values don’t change
I suspect that it’s the 3rd one, but I just want to make sure :slight_smile:

You are correct, I want the previously selected values to persist in the sense that the current view keeps a list of selected items. And then I would like a way for the user to reset everything to a default state. I hope this makes sense.

1 Like

@alex_01

You can reset everything to initial state provided the modifications are made dynamically within the callback and not the global variable where modification becomes permanent as long as the app runs.

Did you try the “copy” suggestion by Chris?.

@raghunath

Did not get the effect I want. Value gets overwritten each time.

What I want is the same as

but with a way to reset. I know you were looking at something similar. Any advice?