Streamlit App button issue: Showing Previous Number After Button Click Instead of New Random Number

I’m building a simple Streamlit app as a demonstration of a larger project I’m working on. The goal is to display a random number between 0-100 and let the user select whether they “like” the number or not. After the user clicks either “Yes” or “No,” the app should store the number and their response in JSON format and then show a new random number immediately.

However, after the user clicks a button, the old number still shows up for another round of selection instead of displaying the next random number. It’s only after clicking a button again that a new number appears, but it ends up associating the response with the new number instead of the old one. Below is the simplified example code I’m using:

import streamlit as st
import random
import json

# Initialize session state to store numbers and user responses in JSON format
if "data" not in st.session_state:
    st.session_state.data = []

# Function to generate the next random number
def get_next_number():
    return random.randint(0, 100)

# Initialize the first number when the app starts
if "current_number" not in st.session_state:
    st.session_state.current_number = get_next_number()

# Function to handle the user response
def store_response(response):
    current_number = st.session_state.current_number
    st.session_state.data.append({"Number": current_number, "Response": response})
    # Generate the next random number immediately after response
    st.session_state.current_number = get_next_number()

st.title("Random Number Preference App")

# Display the current number
st.write(f"Do you like the number **{st.session_state.current_number}**?")

# Layout for the response buttons
col1, col2 = st.columns(2)

# Handling "Yes" button click
with col1:
    if st.button("Yes"):
        store_response("Yes")

# Handling "No" button click
with col2:
    if st.button("No"):
        store_response("No")

# Display the stored responses in JSON format
if len(st.session_state.data) > 0:
    st.subheader("User Responses (JSON Format)")
    st.json(st.session_state.data)

    # Allow the user to download the results as a JSON file
    json_data = json.dumps(st.session_state.data)
    st.download_button(label="Download as JSON", data=json_data, file_name="responses.json", mime="application/json")

Problem:

  • After the first number is shown, if the user selects either “Yes” or “No,” the same number is displayed again.
    • When the next random number is eventually displayed, the previously recorded response (e.g., “Yes” or “No”) is applied to the newly generated number, not the number the user saw when they made the selection.

Steps to Reproduce the Issue (with Screenshots):

  1. Initial Screen:
    When the app starts, it immediately shows a random number (e.g., 65). The user is presented with two buttons, “Yes” and “No,” to select if they like the number.

  2. After Selecting “Yes”:
    After pressing the “Yes” button, the app still shows the same number (65). The user can click “Yes” or “No” again, but it seems that the new random number hasn’t appeared yet.

  1. Next Random Number Appears with the Wrong Response:
    After pressing “Yes” again, a new random number is finally shown (in this case, 44), but the response (Yes) from the previous selection is now associated with the new number, which is not the expected behavior.

What I expect:
- When the user clicks a button (either “Yes” or “No”), the next number should immediately appear, and the response should be recorded for the current number, not the next one.

I’ve tried managing state with st.session_state and experimented with st.experimental_rerun() (though my version of Streamlit doesn’t support it), but I can’t seem to get the app to display a new number right after the button click.

Question:
- How can I make the app show the next random number immediately after the user selects their response, while correctly associating the recorded response with the displayed number?

Adding st.rerun() after the end of store_response fix the problem for me.

        def store_response(response):
            current_number = st.session_state.current_number
            st.session_state.data.append({"Number": current_number, "Response": response})
            # Generate the next random number immediately after response
            st.session_state.current_number = get_next_number()
            st.rerun()

Another way is to use the ‘on_click’ :

import random
        import json

        # Initialize session state to store numbers and user responses in JSON format
        if "data" not in st.session_state:
            st.session_state.data = []

        # Function to generate the next random number
        def get_next_number():
            return random.randint(0, 100)

        # Initialize the first number when the app starts
        if "current_number" not in st.session_state:
            st.session_state.current_number = get_next_number()

        # Function to handle the user response
        def store_response(response):
            current_number = st.session_state.current_number
            st.session_state.data.append({"Number": current_number, "Response": response})
            # Generate the next random number immediately after response
            st.session_state.current_number = get_next_number()
            # st.rerun()

        st.title("Random Number Preference App")

        # Display the current number
        st.write(f"Do you like the number **{st.session_state.current_number}**?")

        # Layout for the response buttons
        col1, col2 = st.columns(2)

        # Handling "Yes" button click
        with col1:
            st.button("Yes", on_click=store_response, args=("Yes",))

        with col2:
            st.button("No", on_click=store_response, args=("No",))

            # Display the stored responses in JSON format
        if len(st.session_state.data) > 0:
            st.subheader("User Responses (JSON Format)")
            st.json(st.session_state.data)

            # Allow the user to download the results as a JSON file
            json_data = json.dumps(st.session_state.data)
            st.download_button(label="Download as JSON", data=json_data, file_name="responses.json",
                               mime="application/json")

It will trigger a rerun by calling the function above

1 Like

thanks that seems to be working, but in my actual app it refreshes the whole UI. Very quick blink