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.

1 Like

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.