Skip to content

Add use_session_state hook for persistent state across WebSocket reconnects - #300

Merged
Archmonger merged 26 commits into
mainfrom
persistent-state
Sep 15, 2026
Merged

Archmonger merged 26 commits into
mainfrom
persistent-state

Conversation

@Archmonger

@Archmonger Archmonger commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Description

Add a dedicated, database-backed hook reactpy_django.hooks.use_session_state(default, key) that persists state so it survives WebSocket reconnects (and, optionally, page reloads).

Problem

On a WebSocket reconnect the client re-sends mount-component, the server creates a fresh Layout, and all in-memory component state (use_state, etc.) is lost. This is especially problematic for deployments that use multiple processes or reactpy-django's built-in round-robin load balancing across hosts, where a reconnect may land on a different worker whose memory does not contain the previous state.

Solution

Add a database-backed hook so state is durable across process restarts and round-robin hosts, unlike an in-memory approach.

from reactpy_django import hooks

def my_component():
    count, set_count = hooks.use_session_state(0, key="counter")
    return html.button({"onClick": lambda _: set_count(count + 1)}, f"Count: {count}")

Design decisions

  • DB-backed (not in-memory) so state is durable across multi-process workers and round-robin hosts.
  • Stable scope identity controlled by REACTPY_SESSION_STATE_MODE:
    • "tab" (default): scoped to the rendered component's UUID (a per-tab/per-component token that is stable across reconnects). Works for anonymous users without django.contrib.sessions and isolates state between browser tabs.
    • "user": scoped to the authenticated user's PK, falling back to a per-tab token for anonymous users.
  • Debounced, periodic writes (REACTPY_SESSION_STATE_SYNC_INTERVAL, default 10s) so rapid state changes (e.g. typing) are coalesced into one DB write; state is also flushed on unmount so the latest value is persisted before a reconnect.
  • Serialized with dill (already a dependency) to support arbitrary serializable Python objects.

Settings added

Setting Default Purpose
REACTPY_SESSION_STATE_MODE "tab" Scope identity (per-tab vs per-user).
REACTPY_SESSION_STATE_SYNC_INTERVAL 10 Seconds between debounced DB flush writes.
REACTPY_SESSION_STATE_MAX_AGE 259200 (3 days) How long stale state is retained before cleanup.
REACTPY_CLEAN_SESSION_STATE True Whether the clean task removes stale session state.

Notes for reviewers

  • The per-tab identity is currently derived from the component UUID generated by the {% reactpy %} template tag. This is stable across reconnects (baked into the rendered HTML) but not across full page reloads. A future enhancement could lift the per-tab token into ReactPy core for a more stable, browser-persistent identity.
  • Tests are provided for the database layer. Hook integration tests (via Playwright) and full documentation are follow-ups; this PR focuses on the implementation and CI correctness.

Checklist

Please update this checklist as you complete each item:

  • Tests have been developed for bug fixes or new functionality.
  • The changelog has been updated, if necessary.
  • Documentation has been updated, if necessary.
  • GitHub Issues closed by this PR have been linked.

By submitting this pull request I agree that all contributions comply with this project's open source license(s).

@Archmonger
Archmonger marked this pull request as ready for review September 14, 2026 23:57
@Archmonger
Archmonger requested a review from a team as a code owner September 14, 2026 23:57
User added 3 commits September 15, 2026 00:48
Add validation for the new REACTPY_SESSION_STATE_* and
REACTPY_CLEAN_SESSION_STATE settings introduced for the
use_session_state hook.
Add reference docs for the new REACTPY_SESSION_STATE_* and
REACTPY_CLEAN_SESSION_STATE settings, and document the
use_session_state hook with an example.
Add 'debounced' and 'picklable' to the docs dictionary, and reword
the 'un-picklable' phrase in the use_session_state docs so the docs
build passes in strict spellcheck mode.
@Archmonger Archmonger linked an issue Sep 15, 2026 that may be closed by this pull request
1 task
…c sync

Change the default REACTPY_SESSION_STATE_SYNC_INTERVAL from 5 to 10
seconds, and allow setting it to 0 to disable periodic syncing. When
0, state is only persisted to the database on unmount (e.g. when a
WebSocket reconnects). Update the docs, changelog, and system check
hints accordingly.
@Archmonger
Archmonger merged commit 27c4c57 into main Sep 15, 2026
23 checks passed
@Archmonger
Archmonger deleted the persistent-state branch September 15, 2026 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

use_session_state hook

1 participant