Multiselect only updating for every second input

When choosing from the multiselectbox below, I find that I have to click the options twice in order for it to register in the streamlit application.

By default the select shows the years 2017-2019.
If I then click ‘2016’ it shows 2016-2019, correctly.
If I then click ‘2015’ it show 2016-2019 once again.
If I then click ‘2015’ once again, it shows 2015-2019, which is correct.

I want the multiselect to default to the latest y1-input always, since the application has multiple pages, which the user navigates in between.

code:
session_state = get(y1=['2019','2018','2017'])

session_state.y1 = st.multiselect("Select year(s)['2013','2014','2015','2016','2017','2018','2019'],session_state.y1)

Can anyone help me figure a solution out?

Hey @Pbennekou, welcome to Streamlit!

I assume you’re using the SessionState gist?

This is some bugginess around the SessionState implementation, and how it interacts with Streamlit’s rerun logic. As a workaround, you can do something like this:

import streamlit as st
from streamlit.ScriptRequestQueue import RerunData
from streamlit.ScriptRunner import RerunException

last_y1 = session_state.y1
session_state.y1 = st.multiselect(
    "Select year(s)",
    ['2013', '2014', '2015', '2016', '2017', '2018', '2019'],
    session_state.y1)

if last_y1 != session_state.y1:
    raise RerunException(RerunData(widget_state=None))

That RerunException will force Streamlit to re-execute your script whenever the session_state is updated.

(This is a temporary solution; we intend to “officially” implement SessionState in an upcoming release.)

3 Likes

Thank you Tim, that worked out exactly as intended!
I will look forward to the “official” implementation of SessionState in the future, but this will do for now :slight_smile:

I seem to have the same problem with a text area. The solution proposed here does not work for me, I guess as I am doing other things with “session” variables here as well.

My current solution is that I present users the message that their next edit will be ignored, which works for a while but is not a really viable route for the future.

Just FYI to make clear that it does not only affect Multiselect widgets.

Keep on the fantastic work :slight_smile:

1 Like

This still seems to be a problem. I have a st.number_input in my script, and it only works correctly on every second press of the step buttons. Every other time it will just reset to the previously displayed value.

I am using my own implementation of something like SessionState, however mine saves the state to the host machine as a .yaml file, because I don’t intend on using the server via the network. Maybe I made the same mistakes as the SessionState creators, or it is a bug in streamlit itself?

Is there a way to adapt @tim 's workaround with the RerunException in such a way that it only reruns the widgets after the thing that was changed? For me it seems to rerun the entire script every time, even if I only change something at the bottom of the page.

Thanks :slight_smile:

I am using the official SessionState but I still encounter this issue for both selectboxes and text_input (version 0.84.1).

The flow is that I load a data set and the first row. The sidebar widgets get populated with the features from the row and the main area shows the observed label and the prediction. The user can change the features and see how it affects the prediction. But if the user tries to change the same feature multiple times then the 2nd, 4th, 6th […] time doesn’t change the feature :thinking:. Only in a row though. Change feature1, then feature2, then feature1 is fine.

When I load a row I put the features into dictionary in st.session_state['data'] (if it doesn’t already exist). Then I point the widgets at the dictionary:

  for cat in categorical:
    cat_values = sorted(df[cat].unique())
    st.session_state['data'][cat] = st.sidebar.selectbox(cat, 
                                                         options=cat_values,
                                                         index=cat_values.index(st.session_state['data'][cat]))
  for txt in text:
    st.session_state['data'][txt] = st.sidebar.text_input(txt,
                                                          value=st.session_state['data'][txt])

That way changing a value doesn’t revert the value in another field. I have buttons for Next, Previous, Random and clicking any of them deletes st.session_state['data'] and it gets recreated with the features from the new row. I see that I could use keys on the widgets but this is code-wise really clean. And I don’t see why it should prevent me from changing the same widget twice in a row (maybe something with hashing?).

Any ideas @tim ?

see this: Unexpected session_state behaviour - Using Streamlit - Streamlit

Thank you. I see what you mean about using keys for widgets but I don’t know how I would repopulate all the widgets with the new data when I load a new line…

EDIT: Oh wait, I think I see it. I can write directly into the state for that key without damaging anything, right? So if I have a function that goes st.session_state['feature'] = new_value then I should be good. I think. Hope.

EDIT2: I anyone has an example app I would be grateful. This doesn’t feel like I’m writing the code as intended.

import streamlit as st


if 'sel_key' in st.session_state : # do not run this the first time
    st.write(f'the selected item is: {st.session_state["sel_key"]}')

st.selectbox('test me',(1,2,3),index=1,key='sel_key') # no explicit session_state here!
1 Like

Quick update: I gave up on this. Using widget keys I couldn’t figure out how to get the widgets populated correctly when I loaded a new line (I would get the first index in a selectbox and I’m pretty sure that the text_input widgets would just never change).
I decided that writing a caveat and moving on was a better use of my time. It’s more important to see the predictions, observed labels, and features than changing the feature values a bunch of times.

Thank you for your time and input ksxx and sorry I couldn’t get it to work (and sorry I don’t have time to write a minimal reproducible example that I could share).

Thanks a lot! I never really understood what the keys were about and didn’t bother, but using them in this case really solves the problem! :grinning:

(And as I had the problem only with one st.multiselect input, applying keys wasn’t too much of an effort in my case.)

I’ve faced the same issue in several multiselect inputs: I have to change available lists for the next one based on previous select and moreover keep all selections after rerun. I was trying to keep everything in st.sessions_state[‘my_own_dict’][‘some_filter_key’] but it worked not each time. Keeping all in form with submit button also did nothing to problem. But after I’ve just generated keys for each form and asked for value in session_state to load as default value. Everything seems to be ok, even within submit form.
Nevertheless session_state with not key values still has problems.

Hi together,

I have a similar problem, namely I have an app which consists of several pages and I want to save the defaults for the multiselect widget in session state so that it is always up to date.

I have to click the options in the multiselect widget 2 times so that it is transferred to the session state.
I have already tried it via callback functions, but I must also pass the current value of the widget, which is not possible as far as I know.

for columns in st.session_state['cardinal']:
	for column in columns:
    	st.session_state.default[column]
        st.session_state.default[column] = st.multiselect(f'{column}', options_cardinal, default=st.session_state.default[column])

Is there any way to use session state in the default options in widgets and keeping it up to date?
Thanks in advance! :slight_smile:

I think you want to use the keys option on the multi select widget,

Let us know if you need more info

Thanks for your fast reply. Unfortunately, there is still a problem when I take the session state as default.

My main goal is that the widget is displayed with the default values I saved last time in session state when I return to the page.

I think this may be related to this post

In that post I added a suggested solution with a link to a working example.

1 Like

I’m still meeting the same problem when I tried both Streamlit 1.24 and 1.25. Also refer this Streamlit Docs Widget update for every second input. Here is my code:

if 'features_chosen' not in st.session_state:
  st.session_state.features_chosen = None

st.multiselect("Which features would you like to select?", features, st.session_state.features_chosen, key="features_selectbox")
st.session_state.features_chosen = st.session_state['features_selectbox']

I need the value of st.session_state.features_chosen to be persisted. Now for selecting or deselecting any option, it sometimes takes effect by one time, most of the time it need twice actions.
Can someone help me to solve it please? Many thanks!

Note that when you interact with the multiselect, the application reruns inmediately,

If you want to explicitly change session state in response to an user action, do it in a callback.

Thank you @Goyo ! I create a ‘on_change’ callback function from my last line of code above, it works now. But comparing to st.multiselect, st.selectbox doesn’t need this callback setting to let the change take effect, it works immediately, I don’t know the reason behind.

You cannot do the same thing that you are doing here with selectbox() because you cannot pass a default value. Depending on what you do exactly it may work as you expect or not. Different widgets can show this kind of problem in different circumstances, but the advice to use callbacks to change state in response to user actions is valid for all widgets.