Widget update removes values from session state

Hi everyone,

I’m encountering a frustrating issue with the st.data_editor component in my Streamlit application. I use this component to allow users to interactively modify a table. However, I’m experiencing intermittent problems where, when switching between columns and entering new values, a rerun callback is triggered. This rerun causes me to lose all the changes and values I’ve just entered.

While some values are saved in session_state, it’s not consistent, and this behavior disrupts the user experience. Ideally, once the application is initialized and the user interacts with the table, all values should be reliably saved in session_state. As it stands, users find themselves repeatedly re-entering values until the table is finally in a state where it can be saved to the database.

Has anyone encountered a similar issue or have suggestions for ensuring that all values are consistently saved? Any help would be greatly appreciated!

Thanks in advance!

Demo Code:


# Placeholder dictionary (you should replace this with your actual dictionary)
dct = {
    "supplier": "Supplier",
    "invoice_number": "Invoice Number",
    "invoice_date": "Invoice Date",
    "due_date": "Due Date",
    "net_amount": "Net Amount",
    "gross_amount": "Gross Amount",
    "payment_status": "Payment Status",
    "due_status": "Due Status",
    "note": "Note",
    "dropdown_options": {"Option1": "Option 1", "Option2": "Option 2"},
    "not_due": "Not Due",
    "due": "Due"
}

def load_or_initialize_session_data(page_data_key, page_key):
    """Load or initialize session state data for the given page."""
    if page_data_key not in st.session_state:
        st.session_state[page_data_key] = db.load_user_data()
    if page_key not in st.session_state:
        st.session_state[page_key] = 0
    return st.session_state[page_data_key]

def app(page="demo"):
    # Assign the specific formats to column types
    column_config = {
        dct.get("supplier"): st.column_config.TextColumn(),
        dct.get("invoice_number"): st.column_config.TextColumn(),
        dct.get("invoice_date"): st.column_config.DateColumn(format="dd/MM/yyyy"),
        dct.get("due_date"): st.column_config.DateColumn(format="dd/MM/yyyy"),
        dct.get("net_amount"): st.column_config.NumberColumn(format="%.2f €", default=0.0),
        dct.get("gross_amount"): st.column_config.NumberColumn(format="%.2f €", default=0.0),
        dct.get("payment_status"): st.column_config.SelectboxColumn(
            options=list(dct.get("dropdown_options").values()), required=True
        ),
        dct.get("due_status"): st.column_config.SelectboxColumn(
            options=[dct.get("not_due"), dct.get("due")],
            required=True,
            default=dct.get("not_due"),
        ),
        dct.get("note"): st.column_config.TextColumn(),
    }

    # Compose a list of all relevant columns
    all_columns = list(column_config.keys())

    # Determine the effective identifier for the page or table
    effective_identifier = page

    # Construct the page data key and page key
    page_data_key = f"{effective_identifier}_data"
    page_key = f"{effective_identifier}_key"

    # Load or initialize session state data
    data = load_or_initialize_session_data(
        page_data_key=page_data_key, page_key=page_key
    )

    # Construct the respective page editor key
    page_editor_key = f"{effective_identifier}_editor_{st.session_state[page_key]}"

    # Display editable data table
    edited_data = st.data_editor(
        data,
        key=page_editor_key,
        num_rows="dynamic",
        column_config=column_config,
        use_container_width=True,
        hide_index=False,
    )

    # Convert edited data to DataFrame and calculate available money
    df = pd.DataFrame(edited_data).reset_index(drop=True)

Debugging info:

  • The app is running locally
  • Streamlit version: 1.38.0
  • Python version: 3.11.9