Mini-tutorial: Initializing widget values and getting them to stick without double presses

Hi,

Have you ever found it frustrating to use widgets with initialized values, getting those values to survive through script reruns and noticed how sometimes you have to double click to get the value to stick? We all have!

This seems to be a common issue amongst new Streamlit users, so I wrote a mini-tutorial app to explain how widgets are used with initialized values and how to make them stick using session state and callbacks.

There are three ways: (1) the most basic where the initial value is not given but the widget is always reset, (2) where it’s initialized but there are issues getting the return value to stick, and finally (3) overcoming all issues with session state and callbacks. (I think the issue in the second case is a Streamlit bug :thinking: or at the very least is counter-intuitive).

You can find my mini-tutorial app code here.

HTH,
Arvindra

7 Likes

Hi @asehmi ,

Thanks for putting this tutorial together, super appreciated.

Did you, the Streamlit team or anyone finally figure out why the 2nd case is happening? It is still happening with Streamlit 1.17.0 :sweat_smile:

@marduk
Not that I know, sorry. Perhaps ask @blackary, who may provide a better explanation than I have given in the tutorial, as to why this case exists.

1 Like

I would expect a double click needed in case 2.

  1. Say the widget is in sync: it has it’s initial value and is outputting its initial value.
st.session_state.A2 = st.number_input(
    label="What is A2?",
    min_value=0, max_value=100,
    value=st.session_state.A2,
    key='num_A2'
)
  1. You click on the widget, so st.session_state.num_A2 is updated instantly and page reloads.
  2. After reloading, st.session_state.A2 is updated, as an output from the widget (namely after the widget is mounted).
  3. On the next click, st.session_state.num_A2 is again updated and the page reloads.
  4. Now although we have a value in st.session_state.num_A2 which should be correct, the widget sees a new initial value in st.session_state.A2 that it didn’t see before, hence Streamlit goes “New Widget!”
  5. Now since the real value we want is stored in a key associated to a widget that Streamlit thinks has been destroyed, what do we get? “Streamlit doesn’t connect back to widgets of the same key if it thinks it’s a different widget.”
    • This is an ongoing issue with multipage use cases. Slap in some key preservation at the top (see below), and presto! You’ve just convinced Streamlit that the value associated to the key does not need to be discarded in the cleanup process.
    • “Well that widget is gone. Let’s get rid of that key value. Oh look, a new widget. No, I don’t already have this key. Let’s make one.”
    • “What widget? This key isn’t associated to a widget. It was just manually written at the top of the page. Oh look! A new widget and I already have a key of the same name. I’ll connect that up right now.”

My so-called “key preservation” for the top of the script:

if 'num_A2' in st.session_state:
    st.session_state.num_A2 = st.session_state.num_A2
6 Likes

Nice trick @mathcatsand, but it sure is a mind bender, isn’t it!? :upside_down_face:

1 Like