Unable to remember state after experimental_rerun()

Hi guys, I’m new to streamlit but very familiar with python.

I have a streamlit app that has a login page and the main app.

    if login_page():
        intro()
        profile_state = create_profile_state(state_obj=st.session_state)
        main_app(
            profile_state=profile_state,
            username=st.session_state["username"],
            preferred_name=st.session_state["preferred_name"]
        )

Here, login_page() returns True or False based on the credentials entered by checking secrets.toml file in the repo. It clears the page once login is True, and initiates the flow of the app.

intro() simply writes static text on the page.

create_profile_state() initiates st.session_state with required keys

main_app() takes the username from st.session_state and loads a profile matching the username from a local json file in the repo.

main_app() displays the profile_state and shows a dropdown to update the information in the state.

Once you click on the dropdown to Update Information, you see a text input box pre-filled with existing information, and a Update button at the end of the dropdown.

When I click update, the main_app() crashes since it doesn’t remember a st.session_state[‘username’] from the login_page()

Update pretty much takes the new input from the dropdown and writes it to the local json file based on the username given. experimental_rerun() is used after this to reload the page with the new information. But main_app() crashes because it doesn’t have a “username” in st.session_state.

Please help. I don’t understand caching and having issues with states management clearly.

Your description is a little too abstract for me to be certain, but I would guess you are running in to the widget cleanup process or the non-statefulness of buttons.

For buttons, they will return true only on the page load resulting from their click and then go back to false. Therefore, there is a problem if you put anything interactive conditioned on a button or you put any rendering commands you want to stay visible while the user continues to interact elsewhere.

For widget keys, when a widget is not rendered on the page whether conditionally or by navigating to another page, the key associated to the widget will be removed from session state. Therefore, if you are wanting data to persist beyond the destruction of a widget, you need to copy its data to a different key (or do a more hacky solution to interrupt the cleanup process).

Say that widget keys are prefixed with an underscore as a convention:

import streamlit as st

def keeper(key):
    #Save data from the widget key to a persistent key
    st.session_state[key] = st.session_state['_'+key]

def retriever(key):
    #Check for persistent key and populate widge key for multipage statefulness
    if key in st.session_state:
        st.session_state['_'+key] = st.session_state[key]

#If you want to declare any initial values, initialize the persistent keys
#directly instead of using the widget's value keyword. This avoids a warning.
if 'my_key' not in st.session_state:
    st.session_state.my_key = 55.5

retriever('my_key')
st.number_input('Example', key='_my_key', on_change=keeper, args=['my_key'])


key = st.selectbox('Widget',['A','B','C','D'])

retriever(key)
st.text_input(key, key='_'+key, on_change=keeper, args=[key])