Skip to content

Memoize stateful AppWrap components#6651

Merged
masenf merged 2 commits into
mainfrom
masenf/memoize-stateful-app-wrap
Jun 10, 2026
Merged

Memoize stateful AppWrap components#6651
masenf merged 2 commits into
mainfrom
masenf/memoize-stateful-app-wrap

Conversation

@masenf

@masenf masenf commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Now that StateProvider and EventLoopProvider are defined as dynamic app_wrap components rather than static providers, it is necessary to auto-memoize (convert to individual react components) any component that depends on state, so that its hooks drop into the auto-memo component, rather than in the parent AppWrap component where the StateProvider has not yet provided the value being depended on.

This is also advantageous generally for preventing whole-page re-renders when an app_wrap component's dependent vars change, since the page component itself is ultimately a descendent of the AppWrap.

Fixes #6650

Now that StateProvider and EventLoopProvider are defined as dynamic app_wrap
components rather than static providers, it is necessary to auto-memoize
(convert to individual react components) any component that depends on state,
so that its hooks drop into the auto-memo component, rather than in the parent
AppWrap component where the StateProvider has not yet provided the value being
depended on.

This is also advantageous generally for preventing whole-page re-renders when
an app_wrap component's dependent vars change, since the page component itself
is ultimately a descendent of the AppWrap.
@masenf masenf requested a review from a team as a code owner June 10, 2026 23:28
@codspeed-hq

codspeed-hq Bot commented Jun 10, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing masenf/memoize-stateful-app-wrap (c0608f4) with main (6a91fbd)

Open in CodSpeed

Footnotes

  1. 8 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 Jun 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a memoization pass for stateful app_wrap components so their React hooks land inside their own memo modules rather than hoisting into the top-level AppWrap function — above the StateProvider where useContext cannot resolve.

  • _memoize_stateful_app_wraps is inserted in compile_app immediately after App._app_root assembles the wrapper chain; it drives MemoizeStatefulPlugin against the assembled root and registers resulting memo definitions on the shared CompileContext, so the existing compile_memo_components pass emits them normally.
  • ErrorBoundary (carrying an on_error event trigger) is the primary built-in wrap that gets extracted; tests are updated to match its memoized tag via regex rather than hardcoding the full inline JSX, and a new test_stateful_app_wrap_is_memoized end-to-end test verifies that useContext(StateContexts…) no longer appears in the AppWrap body while the memo module carries it instead.

Confidence Score: 5/5

The change is well-scoped: it inserts a single extra plugin pass over the already-assembled AppWrap chain and writes memo definitions into the existing compile context dict; all downstream emit logic is unchanged.

The new _memoize_stateful_app_wraps helper correctly passes the real compile_context so memo definitions reach compile_memo_components. The isolated CompilerHooks(plugins=(MemoizeStatefulPlugin(),)) for this pass is intentional — imports are collected afterwards via _get_all_imports() on the returned root. New and updated tests exercise both the structural output and the memo-module content.

No files require special attention.

Important Files Changed

Filename Overview
reflex/compiler/compiler.py Adds _memoize_stateful_app_wraps helper that runs the assembled AppWrap chain through MemoizeStatefulPlugin before emitting the app-root JS, correctly registering memo definitions on the shared compile_context.
tests/units/test_app.py Updates existing tests to account for the memoized ErrorBoundary tag (replacing large inline JSX assertions), adds test_stateful_app_wrap_is_memoized covering the full compile path, and introduces _find_error_boundary_memo_tag helper for robust regex-based tag extraction.
news/6651.bugfix.md Changelog entry for the bugfix.

Reviews (1): Last reviewed commit: "add news fragment" | Re-trigger Greptile

@masenf masenf merged commit 7b3a5c1 into main Jun 10, 2026
106 checks passed
@masenf masenf deleted the masenf/memoize-stateful-app-wrap branch June 10, 2026 23:41
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.

[regression] Using state in AppWrap components: TypeError: null is not an object

2 participants