Why Does My Streamlit Button Require Two Clicks to Trigger an Action?

Hi Streamlit Community,

I’m encountering an issue with a Streamlit app I’m developing. The problem is that my “Simulate” button requires two clicks to trigger its associated first action, but not for the rest. The expected behavior is that upon a single click, the button should execute a function that updates the state of the app and displays a predicted value.

Here’s a simplified version of the problematic part of my code:

import streamlit as st

# Assume necessary imports and setup are done here

# In the actual app, these are filled with user input
input_values = {"example": 1}
selected_datetime = "2023-01-01 00:00:00"

if 'simulated' not in st.session_state:
    st.session_state.simulated = False
if 'input_df' not in st.session_state:
    st.session_state.input_df = pd.DataFrame()

st.write('State of input_df before simulation:')
st.dataframe(st.session_state.input_df)

if st.button("Simulate"):
    st.write('Simulate button clicked')
    st.session_state.input_df = pd.DataFrame([input_values], index=[selected_datetime])
    # Further processing and state updates
    st.session_state.simulated = True

if st.session_state.simulated:
    st.write('Post-simulation code executed')
    # Code to display predictions and other state-dependent elements
  1. Running localy
  2. Not deployed
  3. Newest python and Streamlit versiosn

I’ve already tried the following:

  • Ensuring there are no errors in the console
  • Simplifying the button action code
  • Confirming that Streamlit is up to date
  • Restarting the Streamlit server
  • Testing in different browsers and in incognito mode

Could anyone suggest why the button isn’t working on the first click and what I might do to resolve this issue?

Also, I’ve realized that the same issue of the button requiring two clicks to work is also happening in another part of my application. This second scenario involves an optimization function triggered by a button. Here’s the relevant part of the code:

import streamlit as st
import pandas as pd
import time

# Assuming necessary imports and setup for the model and optimization functions

# In the actual app, 'selected_day_data' and other variables are set earlier in the code
selected_day_data = {"example_data": 1.23}
input_values = {"example_input": 4.56}

# Initialize progress in session state if not present
if 'progress' not in st.session_state:
    st.session_state['progress'] = 0

if st.button('Optimize'):
    st.write('Optimize button clicked')  # This should appear when the button is clicked

    start_time = time.time()
    progress_bar = st.progress(0)
    progress_bar.progress(st.session_state['progress'])

    # Simulating optimization process (replace with actual function call)
    best_values = {'optimized_result': 42}

    # Additional code for processing best_values and updating session state
    # ...

    # Complete the progress and display elapsed time
    progress_bar.progress(1.0)
    elapsed_time = time.time() - start_time
    st.write(f"Optimization completed in {elapsed_time:.2f} seconds.")

# Code related to optimization results rendering
# ...

Thank you for your assistance!

Best,
Gabriel Gomes

Hi @Gabriel_Gomes

Ideally, you can implement a callback function to maintain your app stateful and manage the logic of when to run certain functions better. I strongly recommend reading this article about the differences between approaches using buttons.

Hope it helps!

1 Like

Follow-Up on Button Click Issue in Streamlit

Hi CarlosSerrano,

Thank you for your previous advice on implementing a callback function to maintain a stateful app and manage function executions in Streamlit. I’ve read through the provided article and applied the suggested solutions to my application.

I’m happy to report that using a callback function with the button has solved the issue with the first scenario where I had to click the button twice to trigger the function.

However, I encountered some difficulties with the second scenario, which involves more complex state management. Here’s the code template where I’m facing issues:

def update_progress(progress):
    st.session_state['progress'] = progress

# Function triggered on button click to run optimization
def on_optimize_button_click():
    start_time = time.time()
    progress_bar = st.progress(0)

    # Check and update progress if already in session state
    if 'progress' in st.session_state:
        progress_bar.progress(st.session_state['progress'])

    # Optimization logic here (simplified for brevity)
    # ...

    # Record elapsed time and finalize progress
    elapsed_time = time.time() - start_time
    st.session_state['elapsed_time'] = elapsed_time
    st.session_state['progress'] = 100

    st.write(f"Otimização concluída em {elapsed_time:.2f} segundos.")

# Initialize session state variables if not already present
if 'progress' not in st.session_state:
    st.session_state['progress'] = 0
# Additional session state initializations here
# ...

# Button to trigger optimization
if st.button('Otimizar', on_click=on_optimize_button_click):
    pass

# UI elements to reflect changes in session state
# ...

The expected behavior is that when the ‘Otimizar’ button is clicked, on_optimize_button_click should be called, the progress should be updated, and the UI should display the elapsed time. However, the function only seems to execute after the second click.

Any insights on why this might be happening or what I can do to ensure the optimization runs and updates the UI on a single click would be incredibly helpful.

Thank you for your assistance.

Best regards, Gabriel Gomes

Hello.

Please remove the if statement from the button and try again. Just use st.button

Hello Carlos,

Thank you for your previous response. I followed your recommendation and removed the if statement from the button execution, using directly st.button('Optimize', on_click=on_optimize_button_click). However, I’m encountering a situation where the button click doesn’t seem to trigger the on_optimize_button_click function on the first click. The update to the state is only reflected in the user interface after a second click.

Is there any other aspect of button behavior or session state management that I might be overlooking?

Here is the updated code snippet for reference:

def update_progress(progress):
    st.session_state['progress'] = progress

def on_optimize_button_click():
    start_time = time.time()
    progress_bar = st.progress(0)
    # Other treatments

    # Update session state with the results
    st.session_state['cu_teor'] = cu_teor
    st.session_state['best_values'] = best_values
    st.session_state['progress'] = 100
    st.session_state['elapsed_time'] = time.time() - start_time


    st.write(f"Optimization completed in {st.session_state['elapsed_time']:.2f} seconds.")

# Initialize session state variables if they are not already present
if 'progress' not in st.session_state:
    st.session_state['progress'] = 0
if 'cu_teor' not in st.session_state:
    st.session_state['cu_teor'] = 0
if 'best_values' not in st.session_state:
    st.session_state['best_values'] = {}
if 'elapsed_time' not in st.session_state:
    st.session_state['elapsed_time'] = None

# Define the button that triggers the optimization process
st.button('Optimize', on_click=on_optimize_button_click)

I appreciate your help in advance!

Best regards,
Gabriel Gomes