I am having an issue where only every other text_input value gets set. Regardless whether I use tab or enter or return, the value only “takes” every other time.
Steps to reproduce
Code snippet:
def main():
if 'topic' not in st.session_state:
st.session_state.topic = "0"
print(f"Before: Session State topic: {st.session_state.topic}")
topic = st.text_input(label="Topic", value=st.session_state.topic)
print(f"During: New Value: {topic}")
st.session_state.topic = topic
print(f"After: Session State topic: {st.session_state.topic}")
Run the above code. It displays a single text box, and initializes it to 0 on the first run. Enter a 1 and hit return. Select it again, and enter a 2. Select it again and enter a 3. Again with 4.
Expected behavior:
I would expect that the text box would always “take” the latest text entry, which would get saved into st.session_state.topic. I would expect the log to show:
Before: Session State topic: 0
During: New Value: 0
After: Session State topic: 0
Before: Session State topic: 0
During: New Value: 1
After: Session State topic: 1
Before: Session State topic: 1
During: New Value: 2
After: Session State topic: 2
Before: Session State topic: 2
During: New Value: 3
After: Session State topic: 3
Before: Session State topic: 3
During: New Value: 4
After: Session State topic: 4
Actual behavior:
Instead, every other entry is ignored. The log shows:
Before: Session State topic: 0
During: New Value: 0
After: Session State topic: 0
Before: Session State topic: 0
During: New Value: 1
After: Session State topic: 1
Before: Session State topic: 1
During: New Value: 1
After: Session State topic: 1
Before: Session State topic: 1
During: New Value: 3
After: Session State topic: 3
Before: Session State topic: 3
During: New Value: 3
After: Session State topic: 3
Debug info
Streamlit version: Streamlit, version 1.26.0
Python version: Python 3.8.16
Using Conda? No. PipEnv? No. PyEnv? Yes. Pex? No.
OS version: MacOS 13.5
Browser version: Safari Version 16.6 (18615.3.12.11.2)
Requirements file
streamlit~=1.25.0
Links
Link to your GitHub repo: Use above snippet instead; full app adds needless noise.
Link to your deployed app:
Additional information
If needed, add any other context about the problem here.
I see a very similar post from a year ago here, but it seems to have been closed without comment:
Hi @samkass, see if any one of the following approaches works for you:
Approach #1
import streamlit as st
def main():
if 'topic' not in st.session_state:
st.session_state.topic = "0"
st.session_state.topic = st.text_input(label="Topic")
main()
OR
Approach #2
def callbk():
st.session_state.topic = st.session_state.xyz
def main():
if 'topic' not in st.session_state:
st.session_state.topic = "0"
st.session_state.topic = st.text_input(label="Topic", value=st.session_state.topic, key='xyz', on_change=callbk)
main()
Hi Shawn,
Thanks for the suggestion. The first one doesn’t really work for me, because I actually want to do something like:
def main():
if 'topic' not in st.session_state:
st.session_state.topic = f"{randint(0, 100)}"
randomize = st.button("Randomize")
if randomize:
st.session_state.topic = f"{randint(0, 100)}"
topic = st.text_input(label="Topic", value=st.session_state.topic)
st.session_state.topic = topic
Where it initializes to a random value, and a button will re-randomize it, but a person can override the random value by typing a value explicitly.
The second approach you suggest does indeed work. However, I have about 8-10 fields I want to update, and having a separate callback for each would be a bit cumbersome. I’ll try creating a single callback which synchronizes the text fields with the session_state for all of them, and set them all to the same callback, and see how it goes.
Ideally, I would have loved to not use session_state at all, and just read/write directly to the text_input, but I can’t think of any way to satisfy the ability to explicitly initialize the value, allow a re-randomizer, and allow an explicitly typed value to override it to all work without session_state.
Hi @samkass, for the 2nd approach, you can probably use a common callback and pass the key via the argument parameter (of the widget) to the callback. That way, you can dynamically make the callback work for only an intended widget at any given point in time.
Examining the 2nd approach, it occurred to me that if the callback could get the value from ‘xyz’, that perhaps I should just use “key” instead of “value”+assignment.
The below code seems to work, and uses that approach. (Let’s call this approach 3.) If there’s nothing idiomatically wrong with it, I would think this should be the normal pattern for a text_input which needs to assign to a session_state variable:
def main():
if 'topic' not in st.session_state:
st.session_state.topic = f"{randint(0, 100)}"
randomize = st.button("Randomize")
if randomize:
st.session_state.topic = f"{randint(0, 100)}"
st.text_input(label="Topic", key="topic") # instead of st.session_state.topic = topic, just assign it via "key"
I have incorporated “approach 3” into my larger application, and it seems to work well. It still seems like a bug that receiving the value then setting the session_state manually would lead to “skipped” values, as following the documentation would naturally lead to that pattern. I think the logic of how “value” affects the current text_input if the value is a session_state variable is currently technically undefined, and should probably be fixed to something where the original code works.
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.