streamlit / streamlit

Streamlit — A faster way to build and share data apps.
https://streamlit.io
Apache License 2.0
35.46k stars 3.08k forks source link

Interact with local storage #5105

Open sg-s opened 2 years ago

sg-s commented 2 years ago

ideally, something like this:

# write something to local storage
st.local_storage.a = 1

# reload the session, etc., 

# read it back
a = st.local_storage.a

Community voting on feature requests enables the Streamlit team to understand which features are most important to our users.

If you'd like the Streamlit team to prioritize this feature request, please use the 👍 (thumbs up emoji) reaction in response to the initial post.

jrieke commented 2 years ago

@sg-s What exactly do you want to use that for? Want to know so we can figure out if localStorage is the best way to solve this, or if there's a better/more user-friendly API we can offer. Also, this isn't super trivial to implement on some hosting solutions due to iframes, so we probably won't address this in the near future.

sg-s commented 2 years ago

@jrieke thanks for the response! one concrete use case is for my vocabulary app built using streamlit

i would like to keep track of user scores and performance, and local storage is great because i don't need anything server side, and user data is private.

other uses i can think of:

kaisa-kucherenko commented 2 years ago

Hi! I also use local storage to save the results of the application so that the user can continue where he left off (closed the tab or browser). The local storage was selected because cookies have limits on the size of the stored data. Now I'm doing it with the streamlit_javascript module, but if it was native with streamlit and optimize for it would be great!

sg-s commented 2 years ago

@kaisa-kucherenko can you point me to how you used streamlit_javasript? i struggled with it and wasn't able to get it to work and ran into weird issues where clicking things would cause my app to double load

kaisa-kucherenko commented 2 years ago

@sg-s Yes, I have the same problem, that's why I found this issue and totally support and waiting for this :)

Below is the code that helps to partially solve the problem with loading, through the spinner and a set of values from the local storage to the session state, then the local storage is accessed only once when the session state is empty.

Streamlit also has a problem with entering data in the fields https://discuss.streamlit.io/t/form-submit-works-only-on-odd-tries/29804 in this code a partial solution to this problem too

The solution is far from ideal, but at least slightly overlaps the problem. I hope this will be useful to you

import time
import json
import streamlit as st
from streamlit_javascript import st_javascript

def get_from_local_storage(k):
    v = st_javascript(
        f"JSON.parse(localStorage.getItem('{k}'));"
    )
    return v or {}

def set_to_local_storage(k, v):
    jdata = json.dumps(v)
    st_javascript(
        f"localStorage.setItem('{k}', JSON.stringify({jdata}));"
    )

TOOL_PREFIX = 'name_of_tool'
INPUT_FIELDS = ['first_input', 'second_input']

if st.session_state.get(TOOL_PREFIX):
    saved_result = st.session_state[TOOL_PREFIX]
else:
    with st.spinner():
        saved_result = get_from_local_storage(TOOL_PREFIX)
        # without sleep doesn't always load on time
        time.sleep(0.5)

# this for solving field problem 
# https://discuss.streamlit.io/t/form-submit-works-only-on-odd-tries/29804
for field in INPUT_FIELDS:
    if field not in st.session_state:
        st.session_state[field] = saved_result.get(field, '')

def save_update_state(st_data: dict):
    st.session_state[TOOL_PREFIX] = st_data
    set_to_local_storage(TOOL_PREFIX, st_data)

with st.form(key='test_form'):
    inp = st.text_input(label='Enter some data',
                        key='first_input')
    btn = st.form_submit_button('Go on!')
    if inp and btn:
        saved_result['first_input'] = inp
        st.success('Its ok')
        save_update_state(saved_result)
    elif btn:
        st.warning('Put something in text area')

with st.form(key='test_form_2'):
    inp = st.text_input(label='Enter some data',
                        key='second_input')
    btn = st.form_submit_button('Go on!')
    if inp and btn:
        saved_result['second_input'] = inp
        st.success('Its ok')
        save_update_state(saved_result)
    elif btn:
        st.warning('Put something in text area')

st.write(st.session_state)
sg-s commented 2 years ago

@kaisa-kucherenko thank you so much! i will try this out

jihangjun commented 1 year ago

@kaisa-kucherenko thank you so much! i will try this out I think use st.stop() will better, like this: if not saved_result.ready(): st.stop(), when I use extra-streamlit-cookiemanger, I find communicate with cookie is not easy, it is disturbed by streamlit self , always reload, I tyied setup time.sleep(), but not always work.

sfc-gh-jcarroll commented 4 months ago

I wrote an updated version of what @kaisa-kucherenko provided, also based on https://github.com/toolittlecakes/streamlit_js and work from @toolittlecakes

This is a simple dict-like wrapper

https://gist.github.com/sfc-gh-jcarroll/e73f3ac80dadb5d0f2136d9d949c35a9

You can use it like this:

from st_local_storage import st_local_storage as st_ls

key = st.text_input("Key")
value = st.text_input("Value")
if st.button("Save"):
    st_ls[key] = value

if key:
    f"Current value of {key} is:"
    # This will persist across browser sessions
    st.write(st_ls[key])

It seems to work fairly well in my limited testing. Would love any feedback!

tpougy commented 4 months ago

Any updates on this issue from streamlit team?

Also, thanks to @sfc-gh-jcarroll for the awesome code. It seems very handy.