Streamlit with session_state

Hi team,
Does the new session_state feature resolves the issue of app getting reset while using two buttons? My app’s 2nd button click actually resets the entire app and takes me back to the state where the first button was not pressed.
Does the session_state helps to use both the buttons? Can anyone share an example please?
Thanks!
Disclaimer: Novice streamlit user

Hello @arindam, welcome to the forum!

Yes it does! Now there is many ways to solve this issue.
Let’s take this basic non-working example:

import streamlit as st

data = []

if st.button("Load data"):
    data = ["This", "Is", "My", "Data"]

    if st.button("Uppercase data"):
        data = [item.upper() for item in data]
        st.write(data)

Behind that pattern, the idea is to divide the app in multiple sequential step, eventually taking parameters between steps to choose and filter some data.

So we must make sure to go from one step to another, while keeping our data and without executing the previous steps as our script reruns from the beginning.

import streamlit as st

# An alias for our state
state = st.session_state

# A function to easily go from one step to another
def change_step(next_step):
    state.step = next_step

# Let's initialize our session state
if "data" not in state:
    state.data = []
    state.step = "init"

# Step 1
if state.step == "init":
    st.button("Load data", on_click=change_step, args=["load"])

# Step 2
if state.step == "load":
    state.data = ["This", "Is", "My", "Data"]
    st.button("Uppercase data", on_click=change_step, args=["upper"])

# Step 3
if state.step == "upper":
    state.data = [item.upper() for item in state.data]

# We print our data everytime
st.write(state.data)

1 Like

Thanks for the quick reply @okld . But somehow I still can’t manage to do this. It is still refreshing my app on 2nd button click.
My code is a bit long, not sure how to send the code as a file here.

One thing I have in my code is the 2nd button is inside a nested if condition. The initial if condition has a Submit button and once that submit button is clicked, it does some processing and goes to a download button within that if condition. Now when I click the Download button, it refreshes entire app.
Thanks

You could try the following in your case:

import streamlit as st

if "submitted" not in st.session_state:
    st.session_state.submitted = False

if st.button("Submit") or st.session_state.submitted:
    st.session_state.submitted = True

    if st.button("Download"):
        st.session_state.submitted = False
        st.write("Download clicked!")

If needed, you can upload your code on GitHub Gist (or Pastebin if you don’t have a github account) and share the link here.

3 Likes

@okld @arindam
Thanks for bringing this conversation and addressing this issue, really helpful for beginner Streamlit users like me.

So I am currently facing a challenge. I am working towards building a multi-page like streamlit app with the help of buttons.

From the sidebar when a button like button1 is pressed I want the app to display another set of buttons like bb1 in my case and when that is clicked display some plot.

Similarly I want to display bb2 when button2 is clicked and when bb2 is clicked display some chart.

I have used the code below but here when I click button 1 both bb1 and bb2 are showing up. Could you please help me with a fix or sample code to work on the same.

session_state1 = SessionState.get(checkboxed=False)
session_state2 = SessionState.get(checkboxed=False)

tday = st.sidebar.date_input('Date Input')

lc, mc, rc = st.columns(3)

button1 = st.sidebar.button("Open Interest")
button2 = st.sidebar.button("FII/DII Data")
button3 = st.sidebar.button("Trading Strategy")

if button1 or session_state1.checkboxed:
    session_state1.checkboxed = True

    df = fnodata(tday)
    option = lc.selectbox(
            'Symbol',
            df['SYMBOL'].unique())

    option_exp = mc.selectbox(
            'Expiry DATE',
            df['EXPIRY_DT'].unique())

    option_inst = rc.selectbox(
            'INSTRUMENT',
            df['INSTRUMENT'].unique()) 

    #Getting CMP
    gcmp = get_cmp(df, option)

    # Graph data as per user choice    
    filterdata = filtered_data(df, option, option_exp, option_inst, gcmp)
    bb1 = lc.button("Generate OI Graphs")

    if bb1:
        session_state1.checkboxed = False
        
        oi_chart = oi_chart_graph(filterdata)

        coi_chart = coi_chart_graph(filterdata)

        # Plotting OI Graph
        
        st.plotly_chart(oi_chart)

        # Plotting OI Change Graph
        
        st.plotly_chart(coi_chart)


if button2 or session_state2.checkboxed:
    session_state2.checkboxed = True
    df1 = fiidiidata(tday)

    client_type = lc.selectbox('Client',
            df1['Client Type'].unique())

    
    date_fii = rc.selectbox('Contract Date',
            df1['Date'].unique())
    
    bb2 = rc.button("Generate FII Graphs")

    if bb2:
        session_state2.checkboxed = False
        fii_chart = get_fii_chart(df1)
        st.plotly_chart(fii_chart)
        st.write(df1.tail())


In short, I need help with a sample code that is able to do the following:

  • Display 3 buttons on the sidebar which I have been able to do.

  • Display bb1 when button 1 is clicked, and when this bb1 is clicked display a set of graphs.

  • Display bb2 when button 2 is clicked, and when this bb2 is clicked display another set of graphs.

My code is showing both bb1 and bb2 when button1 is clicked.

Thanks a lot for taking out time and reading this!

Hello @pitanjal,

Is there a reason you use the old SessionState hack instead of the new st.session_state implementation?

That said, SessionState is a singleton, which means that in your app you can have only one instance of it.
So when you create your two session_state1 and session_state2, they actually reference the same SessionState object, and checkboxed will be the same for your two buttons.

Ideally you’d do something like this:

# Using the new st.session_state
if "button1_clicked" not in st.session_state:
    st.session_state.button1_clicked = False
    st.session_state.button2_clicked = False

# Or if for some reason you cannot use it, here's with the old version
session_state = SessionState.get(
    button1_clicked=False,
    button2_clicked=False,
)

Then in your conditions you’d do:

if button1 or st.session_state.button1_clicked:
    # ...

# With the old session state, just drop the `st.`
if button1 or session_state.button1_clicked:
    # ...
2 Likes

Hey @okld ,

Thank You so much for guiding me and helping me with this. I have tried your technique and it is working perfectly now.

Also, thanks for making me aware of the new streamlit st.session_state, I will keep using that from now on.

Thank You so much, once again!

2 Likes

@okld Did someone say that you are a Champ? That was an awesome solution and many thanks for rescuing me from hitting my head in the wall :slight_smile:
I will close this thread, much appreciated.

1 Like