How to prevent app from re-running before submitting form button

I have multiselect and text area input inside st.form and st.form_submit_button. After an output has been generated (selected input option, entered text and clicked the “Extract data” button) I would like the output to be displayed until the “Extract data” button is clicked on again. However, it seems that changing input in multiselect causes the app to re-run and clear the output before clicking the button.

Below is an example of the code

# Import libraries
import streamlit as st
from time import sleep

# User input
with st.form('input'):
    selected_options = st.sidebar.multiselect(
            'Select option:', ['Option1', 'Option2', 'Option3'], default='Option1')
    text = st.text_area("Paste text here", height=350)  
    submit_button = st.form_submit_button(label="Extract data")    

 
# Extract data on submit 
if submit_button:
        # Check that text field is not empty
        if not text.strip():
            st.error('WARNING: Please enter text')
        else:
            with st.spinner(text = 'Extracting information...'):
                sleep(3)
                st.write('You selected: {}'.format(selected_options))

Python 3.8.0
Streamlit 1.11.1
I am using conda on Windows 10
Chrome Version 103.0.5060.134 (Official Build) (64-bit)

HI @Jurgita! Thanks for your question. It’s a big hard to be sure of what the indentation of your code should be – is this right?

import streamlit as st
from time import sleep

with st.form('input'):
    selected_options = st.sidebar.multiselect('Select option:', ['Option1', 'Option2', 'Option3'], default='Option1')
    text = st.text_area('Paste text here', height=350)
    submit_button = st.form_submit_button(label='Extract data')

if submit_button:
    # Check that text field is not empty
    if not text.strip():
        st.error('WARNING: Please enter text')
    else:
        with st.spinner(text = 'Extracting information…'):
            sleep(3)

st.write('You selected: {}'.format(selected_options))

Hi @Jurgita

One way to save the state is using session_state. I tried this code and seems to be what you need:

if "option" not in st.session_state:
    st.session_state["option"] = None


with st.form("input"):
    selected_options = st.sidebar.multiselect(
        "Select option:", ["Option1", "Option2", "Option3"], default="Option1")
    text = st.text_area("Paste text here", height=350)
    submit_button = st.form_submit_button(label="Extract data")

if submit_button:
    # Check that text field is not empty
    if not text.strip():
        st.error("WARNING: Please enter text")
    else:
        with st.spinner(text = "Extracting information…"):
            sleep(3)
            st.session_state["option"] = selected_options
            st.write("You selected: {} - inside submit_button".format(selected_options))
            st.write(text)

if st.session_state["option"] is not None:
    st.write("You selected: {} - outside submit_button".format(st.session_state["option"]))

However, it seems that changing input in multiselect causes the app to re-run and clear the output before clicking the button.

Yes, this is a fundamental property of the Streamlit architecture. You can use cache, session_state or IO operations to create an state machine or applications that preserve the state and other logics. I recommend to read this comment that is related about this characteristic

Hi @blackary - apologies for the messy formatting. What you have posted is correct, and I have now also updated the code in my original question.

It seems to me that there is a bug with st.sidebar.multiselect. I don’t get this issue if I:

  1. move multiselect from sidebar to the top of the page - replace st.sidebar.multiselect with st.multiselect.

  2. Re-write the code using ‘with’ notation:

# User input
with st.form('input'):
    with st.sidebar:
        selected_options = st.multiselect('Select option:', ['Option1', 'Option2', 'Option3'], default='Option1')
    text = st.text_area("Paste text here", height=350)  
    submit_button = st.form_submit_button(label="Extract data") 

@Jurgita Huh. That’s pretty interesting. I’m not sure what’s going on there – some interaction between the form context manager and the sidebar context manager, I suppose. It does seem like there’s some unintended behavior there. My suggestion, if you want the selected text to always be shown, then @fhtsibuya’s suggestion of using session state to cache whatever was previously extracted, and then display it outside the if submit_button: block is a good idea. When you have some set of data to be saved across app runs, then session_state is often a good choice for storing that data.

1 Like

@Jurgita I’ve reported this as an issue on github Different behavior of st.selectbox in a context manager and not in a context manager · Issue #5092 · streamlit/streamlit · GitHub

1 Like