Warning is raised when saving widget values in Session State for st.date_input()

Hello,

If you try to execute the following code, interaction with the number_input will not cause any problems. However, the very first interaction with a date_input will raise a warning, “The widget with key “…” was created with a default value but also had its value set via the Session State API.”

from datetime import date
import streamlit as st

def create_date_period(min_date, max_date) -> list[date,date]:

    left_box_date, right_box_date = st.sidebar.columns(2)

    if "start_date" in st.session_state:
        load_value("start_date")

    start_chosen_date = left_box_date.date_input(
        "Start period",
        min_date,
        min_date,
        max_date,
        format="DD/MM/YYYY",
        key="_start_date",
        on_change=store_value,
        args=["start_date"]
    )


    if "end_date" in st.session_state:
        load_value("end_date")

    end_chosen_date = right_box_date.date_input(
        "End period",
        max_date,
        start_chosen_date,
        max_date,
        format="DD/MM/YYYY",
        key="_end_date",
        on_change=store_value,
        args=["end_date"]
    )

    if "my_key" in st.session_state:
        load_value("my_key")

    st.sidebar.number_input("Number of filters", key="_my_key", on_change=store_value, args=["my_key"])


    return [start_chosen_date, end_chosen_date]


def store_value(key: str) -> None:
    st.session_state[key] = st.session_state["_"+key]

def load_value(key : str) -> None:
    st.session_state["_"+key] = st.session_state[key]


create_date_period(date(2023,2,1),date(2023,5,1))

I read here that the solution would be to do this :

from datetime import date
import streamlit as st

def create_date_period(min_date, max_date) -> list[date,date]:

    left_box_date, right_box_date = st.sidebar.columns(2)

    if "start_date" in st.session_state:
        load_value("start_date")
        start_chosen_date = left_box_date.date_input(
            "Start period",
            min_value=min_date,
            max_value=max_date,
            format="DD/MM/YYYY",
            key="_start_date",
            on_change=store_value,
            args=["start_date"]
        )
    else:
        start_chosen_date = left_box_date.date_input(
            "Start period",
            value=min_date,
            min_value=min_date,
            max_value=max_date,
            format="DD/MM/YYYY",
            key="_start_date",
            on_change=store_value,
            args=["start_date"]
        )

    if "end_date" in st.session_state:
        load_value("end_date")
        end_chosen_date = right_box_date.date_input(
            "End period",
            min_value=start_chosen_date,
            max_value=max_date,
            format="DD/MM/YYYY",
            key="_end_date",
            on_change=store_value,
            args=["end_date"]
        )
    else:
        end_chosen_date = right_box_date.date_input(
            "End period",
            value=max_date,
            min_value=start_chosen_date,
            max_value=max_date,
            format="DD/MM/YYYY",
            key="_end_date",
            on_change=store_value,
            args=["end_date"]
        )

    if "my_key" in st.session_state:
        load_value("my_key")

    st.sidebar.number_input("Number of filters", key="_my_key", on_change=store_value, args=["my_key"])


    return [start_chosen_date, end_chosen_date]


def store_value(key: str) -> None:
    st.session_state[key] = st.session_state["_"+key]

def load_value(key : str) -> None:
    st.session_state["_"+key] = st.session_state[key]


create_date_period(date(2023,2,1),date(2023,5,1))

However, I don’t find this to be a good workaround as it requires duplication of code.
So I’d like to know if there’s a better solution or if a patch is in the works?

store_value() and load_value() functions are taken from the documentation

Thanks in advance,

The use of load_data and save_data are more about preserving the value of the widget across multiple pages specifically. If that’s what you need, the introduction of st.Page and st.navigation provided a different way to accomplish that by putting widgets in the entrypoint file when they need to be stable across all pages.

However, if you are only trying to manipulate the value of widget, the key takeaway is to not use the default value setting. Instead, set a default value through session state and assign the key to the widget. When you need to change the value of the widget, just change the value in Session State which will then reflect when the widget is re-rendered.

So basically, initialize “end_date” in Session State with your desired default value and then you don’t need a conditional to check whether or not the value exists and you can just render the widget with that key (and no inline-declared default value). Or do you have some other requirements that’s getting in the way?

Thanks a lot for your answer.
Indeed, my purpose is to preserve the value of the widget across multiple pages, specifically.
Actually, I deal with multipage with pages/directory method. So I will have a closer look to the st.Page and st.navigation method.
Thanks.

Got it. If you upgrade to the new multipage architecture, your entrypoint file can become like a picture frame for your pages: put all your elements that you want stable across pages in the entrypoint file, then they will remain stable as you switch between pages.