aghasemi / streamlit_js_eval

A custom Streamlit component to evaluate arbitrary Javascript expressions
MIT License
75 stars 12 forks source link

streamlit_js_eval does not run at every update #8

Closed hoegge closed 4 months ago

hoegge commented 5 months ago

When I try to get the window size, it is not updated every time the streamlit page is rerun - I understand changing the window size in browser does not trigger rerun, but why does this not update

x = streamlit_js_eval(js_expressions='window.outerWidth', key='WIDTH',  want_output = True,)
st.write(f"Width is {x}")
y = streamlit_js_eval(js_expressions='window.outerHeight', key='HEIGHT',  want_output = True,)
st.write(f"Height is {y}")
aghasemi commented 5 months ago

Hi. How can I reproduce the issue? Can you provide some instructions maybe?

hoegge commented 5 months ago

Try this:

# Streamlit app
def main():

    st.subheader("Test click")

    x = streamlit_js_eval(js_expressions='window.outerWidth', key='WIDTH',  want_output = True,)
    st.write(f"Width is {x}")
    y = streamlit_js_eval(js_expressions='window.outerHeight', key='HEIGHT',  want_output = True,)
    st.write(f"Height is {y}")

    st.write(random.random())
    if st.button("Click me"):
        st.rerun()

main()

and then resize the browser in-between clicking the button.

hoegge commented 5 months ago

@aghasemi Have you tried?

aghasemi commented 5 months ago

Hi @hoegge . I just checked. I could reproduce the problem indeed.

First of all, I have very little knowledge about the internals of Streamlit and much less about this st.rerun function. I looked into the documentation and it seems using is not considered a good idea there as well.

However, inspired by the code sample in the documentation of rerun, I modified your example to the following snippet:


import streamlit as st, random
from streamlit_js_eval import streamlit_js_eval
import datetime

# Streamlit app
def main():

    if "value" not in st.session_state:
        st.session_state.value = 0
    st.subheader(f"Test click {datetime.datetime.now()} {st.session_state.value}")

    pepper = st.session_state.value

    x = streamlit_js_eval(js_expressions='window.outerWidth', key=f'WIDTH-{pepper}',  want_output = True,)
    st.write(f"Width is {x}")
    y = streamlit_js_eval(js_expressions='window.outerHeight', key=f'HEIGHT-{pepper}',  want_output = True,)
    st.write(f"Height is {y}")

    st.write(random.random())
    if st.button("Click me"):
        st.session_state.value += 1
        st.rerun()

main()

, which seems to actually solve the issue!

So, what is apparently happening is that after rerun is called, Streamlit somehow decides that the SJE "widgets" have not changed and hence need not be called (I have no idea what the criteria here is). To force that, you need to make the widget keys change for each rerun, which I did using a counter stored in the session_state.

Still, as mentioned in the Stream documentations too, you may want to achieve your logic in a way other than using rerun.