Repository navigation
fix: flatten providers for large state trees - #7441
FarhanAliRaza merged 23 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
[High impact] The PR appears safe to merge; the outstanding hot-refresh finding is fixed. SummaryThe PR replaces nested state providers with one shared store, keeping provider depth constant as the state tree grows.
Reviews (21) · Last reviewed commit: "fix: merge new default fields into live ..." · Reviewed by Greptile |
There was a problem hiding this comment.
All reported issues were addressed across 3 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Merging this PR will not alter performance
|
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
All reported issues were addressed across 9 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
View guided diff | Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 5 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 5 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
|
The fix for #7429 replaces the per-substate providers with a shared store. The compiler rewrite preserves legacy reads in compiled Reflex code, but it cannot rewrite independently bundled third-party components using |
There was a problem hiding this comment.
All reported issues were addressed across 8 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
No, accessing the state content directly via javacript not generated by the reflex compiler is not a supported path. |
Drop the compile-time JavaScript rewriter that turned useContext(StateContexts.x) into a shared-store read. Reflex now generates useStateContext(...) for every state read on each compile, so the rewriter only served hand-written hooks. It missed dynamic components and asset JS, where legacy reads silently became null. Remove the StateContexts export and getStateContext as well, so any remaining legacy read fails the build with a missing-export error instead of reading null. Add a breaking news fragment with the migration to useStateContext.
|
I pushed 6f0117c. It removes the legacy Why:
What changed:
Tested: an app with 3000 extra states renders with no errors. A delta still re-renders only the components that read the changed substate. The focused unit tests, ruff, and pyright pass. |
There was a problem hiding this comment.
All reported issues were addressed across 9 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
- Raise reflex's reflex-base floor to 0.10.1.dev0: code_uses_state_contexts looks for useStateContext(, which only the new reflex-base generates. - Drop the unrelated greenlet change in examples.yml. - Compare applyDelta's result with the current state instead of pre-checking the delta, so a deep-equal delta no longer notifies. - Skip syncInitialState when the store already holds that initial state. - Serve the server render from a read-only store. - Remove the duplicate listener set creation and use one useMemo in useStateContext. - Mark the news fragments as bugfix with per-package text. - Make the JS store test use a structural-sharing applyDelta stub, subscribe by state name, and cover an equal list delta.
|
I pushed 1455221. It addresses the review findings on the shared state store:
Tested: in a test app, the render count of each component is the same as on the previous head. Hot reload still adds new states and fields. The focused unit tests, ruff, pyright, and |
There was a problem hiding this comment.
All reported issues were addressed across 9 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
masenf
left a comment
There was a problem hiding this comment.
i really like this idea. it reduces the number of contexts and useState calls overall and it opens the door eventually for per-var subscriptions.
Attach the test listeners through the subscribe callback the hook passes to useSyncExternalStore, so the test fails if the hook subscribes to the wrong substate.
createStateStore takes the initial state and registers substates through one addSubstates path, which hot reload calls for the substates of new state classes. Fields added to existing substates are not merged here: they arrive with the hydrate delta after the backend restarts.
mergeInitialState adds new substates and gives existing substates the fields of the recompiled initial state at their defaults, keeping live values. A substate is replaced and notified only when it gains a field, so the mount-time call changes nothing.
Closes #7429
Summary
Flatten the state provider tree on both the client and server. A single shared state-store context keeps provider depth independent of the number of substates, including during development SSR.
Generated state reads subscribe to their substate with
useSyncExternalStore. A delta applies immutably to only that substate and notifies only its subscribers, avoiding a full state-map copy or rebuilding all provider elements on every update. The external-store hook gives React consistent snapshots during concurrent rendering; the store is changed only by event dispatch, never during render.Testing
git diff --checkpassed.