Skip to content

Create separate context providing components for each state - #6181

Merged
masenf merged 8 commits into
mainfrom
masenf/memoize-state-provider-separately
Sep 19, 2026
Merged

masenf merged 8 commits into
mainfrom
masenf/memoize-state-provider-separately

Conversation

@masenf

@masenf masenf commented Mar 16, 2026 •

Copy link
Copy Markdown
Collaborator

The generated StateProvider currently owns every substate reducer, so each delta re-renders that combined provider and recreates the provider elements for all substates. Give each substate its own reducer and provider so a delta only re-renders the providers it touches. Consumer updates continue to depend on their context values.

Dispatchers register into a stable shared registry during the layout phase, before the event loop connects the WebSocket. DispatchProvider's memo depends only on children, since the registry ref has a stable identity.

Validation: 282 compiler tests, all eight router browser cases across development and production modes, and all pre-commit hooks pass. The new browser case extends the existing router app: its initial on-load event updates two sibling substates, and the test verifies both rendered values plus a single received delta containing both updates. It replaces the source-string test that did not establish runtime ordering.

@codspeed

codspeed Bot commented Mar 16, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 67 untouched benchmarks
⏩ 17 skipped benchmarks1


Comparing masenf/memoize-state-provider-separately (8b23c9a) with main (702f675)

Open in CodSpeed

Footnotes

  1. 17 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@greptile-apps

greptile-apps Bot commented Mar 16, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge with no outstanding actionable findings.

Summary

This PR restructures generated client state contexts so each substate has an independent reducer and provider, reducing unnecessary browser re-renders.

  • Adds a stable shared dispatcher registry with layout-phase registration before the event loop connects.
  • Adds browser coverage for an initial delta updating two sibling substates.
  • Revises the performance news fragment to describe the downstream benefit rather than implementation details.

Reviews (9) · Last reviewed commit: "Describe state provider performance in u..."

Comment thread reflex/compiler/templates.py Outdated
Comment thread reflex/compiler/templates.py Outdated
@masenf
masenf marked this pull request as draft March 16, 2026 19:33
Instead of combining all states and dispatchers into the same component
(which is the re-render domain), separate each state into its own provider
component and combine those into StateProvider. Since StateProvider doesn't
directly depend on each state's context, it doesn't have to re-render just
because some substate changed.

- `StateProvider` keeps its exported name -- it is the app-wrap tag in
  `reflex_base.components.state_context` -- and is now a pure composer; the
  per-substate reducer lives in a new `SubstateProvider`.
- `DispatchProvider` publishes the plain registry object (`ref.current`)
  rather than the ref wrapper, so reads and writes agree.
- Dispatcher registration runs in a layout effect. `EventLoopProvider`
  mounts below the state providers, so its passive effect connects the
  socket before any ancestor passive effect would have registered a
  dispatcher, and `state.js` treats a delta for an unregistered substate as
  a fatal state mismatch. Falls back to `useEffect` on the server, where
  `useLayoutEffect` warns.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015aV7hXqYysQQCFAu7dKxNX
@masenf
masenf force-pushed the masenf/memoize-state-provider-separately branch from 844077c to 7473030 Compare August 26, 2026 07:13
@masenf
masenf marked this pull request as ready for review August 26, 2026 07:42
@masenf
masenf requested a review from a team as a code owner August 26, 2026 07:42

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Re-trigger cubic

@masenf

masenf commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

The final CodSpeed check flagged test_var_access[mutable_dict] (3.22% lower efficiency) with a different-runtime-environment warning. This PR changes the generated state-provider template; the measured Python mutable-proxy code is unchanged. A matched local comparison gave 1.472 ms on main and 1.492 ms here (about 1.36%, below the reported change). These short wall-clock samples cannot establish a causal regression. I have rerun the performance job and am keeping unrelated proxy work out of this PR.

@FarhanAliRaza FarhanAliRaza left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this PR in a real app against a main baseline. The two builds differ only in templates.py.

The app has a root state, sibling substates, a nested substate, a computed var, an event that changes two substates through get_state, a generator event, and two pages with on_load. I added 300 generated substates to make the provider cost visible. A React commit hook counted the components that rendered in each commit and recorded the commit duration.

On main, each delta renders StateProvider and the one consumer. With this PR, each delta renders one SubstateProvider and the one consumer. Consumer renders are the same on both builds. With 310 substates, the median commit duration goes from 1.1 ms to 0.2 ms over 200 clicks.

I also exercised a two-substate delta, a 50-click burst, client navigation between the pages, a hard load on the second page, and a dev hot reload that adds a state var. All deltas dispatched correctly and the browser console stayed clean. A frontend export succeeds.

The requested changes are in the inline comments.

Comment thread packages/reflex-base/news/6181.performance.md Outdated
Comment thread tests/units/compiler/test_compiler.py Outdated
Comment thread packages/reflex-base/src/reflex_base/compiler/templates.py Outdated
Comment thread packages/reflex-base/news/6181.performance.md Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/reflex-base/news/6181.performance.md">

<violation number="1" location="packages/reflex-base/news/6181.performance.md:1">
P3: The fragment names the generated file `utils/context.js`, but the compiler writes it with a JSX extension: `reflex/compiler/utils.py:871` emits `get_web_dir() / (constants.Dirs.CONTEXTS_PATH + constants.Ext.JSX)`, where `CONTEXTS_PATH` is `"utils/context"` (packages/reflex-base/src/reflex_base/constants/base.py:40). Update the fragment to `utils/context.jsx` so the changelog entry matches the shipped file.</violation>
</file>

Comment thread packages/reflex-base/news/6181.performance.md Outdated
@masenf
masenf merged commit 4cba004 into main Sep 19, 2026
125 checks passed
@masenf
masenf deleted the masenf/memoize-state-provider-separately branch September 19, 2026 00:46
masenf pushed a commit that referenced this pull request Sep 19, 2026
… verified; FINDING-023 latch re-confirmed, 024, 025)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeyxWsuC9hqSKq8YcEwZyY
masenf pushed a commit that referenced this pull request Sep 21, 2026
…ng noise per re-verification)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeyxWsuC9hqSKq8YcEwZyY
@masenf masenf added the perf Performance-improving changes label Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

perf Performance-improving changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants