Use same key for a placeholder widget being updated in a loop

When more than one widget is made with the same key, there is a duplicate widget id error, and this is expected. However, when a placeholder is made and updated with the same widget twice, the error is still raised, despite there technically only being one widget. Here is the example:

import streamlit as st
import time
placeholder = st.empty()

while True:
    placeholder.text_area("Hi", key="placeholder")
    time.sleep(5)

I tried amending this by deleting from the cache but the same thing happens:

import streamlit as st
import time
placeholder = st.empty()

while True:
    if "placeholder" in st.session_state:
        del st.session_state["placeholder"]
    placeholder.text_area("Hi", key="placeholder")
    time.sleep(5)

This is the pattern that I use in a dashboard I’m making, and it’s important that I set a key, because I want to get the value in a callback function and I don’t believe there is another way to pass a widget’s value into its callback. I want to use a callback to leverage the “on change” nature of it, rather than detect a change with the returned string of a text_area, although that may be the only way I can work around this.

I am afraid the widget state persists in some way until the script is run again and that’s why you can’t instantiate another one with the same key.

You might use a UUID as the key but if I am right then that would be a memory leak, so don’t do it unless you can ensure the number of iterations has an upper bound.

placeholder.text_area("Hi", key=uuid.uuid4())

OTOH do you really need a loop and replacing the widget? This code seems to achieve the same.

st.session_state["placeholder"] = ""
st.text_area("Hi", key="placeholder")
time.sleep(5)
st.experimental_rerun()

The loop fetches data from databases, would I be able to update my dashboard in your suggestion?
Edit: logically, it seems like I would be able to. Wouldn’t this be reloading all my other UI though?

I also just thought of assigning a random key and passing that in the callback, but I might not have an upper bound and did not consider the memory usage

This is still an issue for me, as using uuid did actually cause a memory leak unfortunately. Looking for other suggestions.

Didn’t try any code myself, however just a long shot regarding the memory leak: Did you try to garabage collect in between script runs? Maybe a simple gc.collect() will help?

Other than that, I believe currently the only solution is to st.experimental_rerun(). Unfortunatelly, there is currently no way to only update a single widget.

I don’t see why my suggestion of rerun the app instead of having a loop would not work for you. Reloading the UI should not be a problem, that is how streamlit is designed to work.

With the dashboard paradigm, you render everything, and then only update what you need to. Reloading everything is not an acceptable solution.

That is a problem when you have elements that take a long time to render, like complex charts with lots of data. But replacing widgets forever willl leak unbounded amounts of memory.

Maybe a mixed approach, where a rerun is triggered every now and then to free memory would work. Or being creative with the dashboard design: simpler widgets are faster to render and there can be different views so that not everything needs to be on sight at the same time.

I made an account just to thank you for this solution =)))