Preserve state on hot reload - #3896
Conversation
…oads When enabled, the renderer records UI-driven prop edits, set_props updates (clientside, serverside and websocket) and memory-type dcc.Store writes as [newVal, originalVal] pairs. Just before a hot reload - soft RELOAD dispatch or full page reload - they are snapshotted (in memory + sessionStorage), then re-applied to the incoming layout unless a prop's initial value changed in the reloaded code, in which case the new code wins. Unmatched edits stay pending so components inserted by callbacks (e.g. pages content) are restored too. The snapshot is only written when a hot reload fires and consumed on use, so a manual browser refresh still resets the app. Off by default; enable with dev_tools_hot_reload_preserve_state=True or DASH_HOT_RELOAD_PRESERVE_STATE=true. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
I dont know if this would be worth it or if people would be interested, but could expand this to keep the current state of executed callbacks for seeing in the callback graphs. |
|
This is a cool feature, I will find this useful in my own work! then clicking the button in App A, will cause a problem in App B. |
…eload # Conflicts: # CHANGELOG.md # dash/dash-renderer/src/observers/executedCallbacks.ts
Switching between two apps served on the same URL could let one app's preserved UI edits (n_clicks included) be restored into the other, since the sessionStorage snapshot was keyed only by pathname. Reuse the per-page-load end_id for this: when preserve_state is on, persist end_id to disk keyed by the app path (platformdirs with a dependency-free fallback) so it stays stable across the reload but unique per app, then scope the sessionStorage snapshot by it. A different app gets a different end_id and so a different key, and can never consume another app's state.
|
|
@KoolADE85 Good catch, that's a real hazard. I've scoped the preserved state per app: when `preserve_state` is on, the server persists this page's `end_id` to disk keyed by the app path (stable across the reload, unique per app), and the renderer keys its sessionStorage snapshot by it. A different app served on the same URL gets a different `end_id`, so it reads a different key and can never restore another app's state — your App A button click can't reach App B, `n_clicks` included. It reuses the same signed `end_id` token we already mint per page load for background-callback handles. |



Add dev_tools_hot_reload_preserve_state: keep UI state across hot reloads
When enabled, the renderer records UI-driven prop edits, set_props
updates (clientside, serverside and websocket) and memory-type dcc.Store
writes as [newVal, originalVal] pairs. Just before a hot reload - soft
RELOAD dispatch or full page reload - they are snapshotted (in memory +
sessionStorage), then re-applied to the incoming layout unless a prop's
initial value changed in the reloaded code, in which case the new code
wins. Unmatched edits stay pending so components inserted by callbacks
(e.g. pages content) are restored too. The snapshot is only written when
a hot reload fires and consumed on use, so a manual browser refresh
still resets the app.
Off by default; enable with
dev_tools_hot_reload_preserve_state=TrueorDASH_HOT_RELOAD_PRESERVE_STATE=true.