Repository navigation
ENG-12682 Let React Compiler optimize generated components - #7694
FarhanAliRaza wants to merge 3 commits into
Conversation
Replays #7272 onto main. That PR auto-closed when its base branch was deleted after #7234 merged; its own changes were the diff between the base-branch head it last merged (07aa53c) and its head (4f3e5db), covering commits c01f4d4, 755ddba and 4f3e5db. Change the patterns Reflex emits that made React Compiler bail out: register refs through a useRegisteredRef hook instead of writing to refs during render, wrap generateUUID in an inline useMemo callback, list only referenced bindings as useCallback dependencies, disable function outlining, and describe Emotion's jsx() to the compiler as JSX. Pin the experimental compiler, which accepts refs passed to jsx(). Add an auto_memoize config option (default True). False removes MemoizeStatefulPlugin from page compilation so the compiler memoizes whole pages; app-root memoization is unchanged. Conflicts with main: #7586 removed hook positions, so the ref hook no longer passes VarData(position=INTERNAL); #7435 removed backend_state_mismatch from state.js, so it stays removed. Claude-Session: https://claude.ai/code/session_01LwJUCTv3FVWzi1UoUpyMLT
There was a problem hiding this comment.
All reported issues were addressed across 20 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
[Medium impact] The PR appears safe to merge; no blocking issues were found. SummaryThis PR lets React Compiler optimize more generated components and adds
No actionable new issues were found. No previous review threads were supplied. Reviews (2) · Last reviewed commit: "Run the auto_memoize compile_app test as..." · Reviewed by Greptile |
Merging this PR will not alter performance1 warning
Comparing |
Export GENERATED_EVENT_CALLBACK from the React Compiler adapter and test it against the names get_memoized_event_triggers produces, so a rename on either side fails a test instead of silently restoring compiler bailouts. Drop the Unicode stale-callback claim from the reflex-base fragment: main never had that bug. Move inline test imports to module level.
Without dry_run, REFLEX_SKIP_COMPILE or a leftover .web/nocompile file made compile_app return before building the page plugins, and the test wrote into the working directory's .web.
Supersedes #7272. That PR was stacked on #7234 and auto-closed, unmerged, when its base branch was deleted after #7234 merged. This PR replays its changes onto current
main: the diff between the base-branch head it last merged (07aa53cb6) and its head (4f3e5dbba). Farhan is the commit author.All Submissions:
Type of change
docs/advanced_onboarding/configuration.md)Changes
React Compiler bailed out on most generated pages and memo wrappers because of patterns Reflex emits. This PR changes those patterns. It also adds an
auto_memoizeoption, so an app usingreact_compilercan let the compiler memoize whole pages instead of Reflex's generated memo wrappers.refs["ref_x"] = ref_xduring render ("This value cannot be modified")const ref_x = useRegisteredRef("ref_x"). The hook lives inutils/state.js, outside compiler scope, and keeps the same registry behavior.useMemo(generateUUID, [])("Expected the first argument to be an inline function expression")useMemo(() => generateUUID(), [])useCallbackdeps listaddEvents,ReflexEvent, and unused hook bindings such assetFilesById("Found extra memoization dependencies")(...args) => addEvents(...)handlers crash function outlining ("Expected temporaries to be promoted")enableFunctionOutlining: falsejsx(type, props, ...children)from Emotion is an unknown function, so compiled pages cannot cache elementsmoduleTypeProviderdescribingjsxlike JSX syntax: frozen arguments, frozen resultThe experimental compiler (
0.0.0-experimental-a1856f3-20260507) is required, because 1.0.0 still rejects refs passed tojsx()and optional calls in ternary tests. This is still npm's currentexperimentaltag, andREACT_COMPILER_DEV_DEPENDENCIESis the only pin;@babel/corestays at main's7.29.7.auto_memoize=FalseremovesMemoizeStatefulPluginfrom page compilation. App-root memoization (_memoize_app_root) is unchanged.Conflicts with main and how they were resolved
The patch applied cleanly everywhere except two hunks:
component.py_get_ref_hook: fix: render hooks in declaration order instead of by HookPosition #7586 removed hook positions, so main's ref hook no longer passesVarData(position=HookPosition.INTERNAL). The resolution keepsuseRegisteredRefand drops the position.state.js: the PR's hunk carriedlet backend_state_mismatch = false;as context, but fix(state): recover from unknown frontend substates #7435 removed that from main. The resolution adds onlyuseRegisteredRef.I also checked the memo-compiler changes on main that the patch didn't textually touch: mirrored memo modules, auto-memo keying in
memoize.py, andcompiler.py.default_page_pluginsis still the only place page compilation addsMemoizeStatefulPlugin, so theauto_memoizeswitch still covers every page path.Measurements (from the original PR #7272)
Production builds, Chromium, direct state dispatch, 11 alternating rounds × 100 updates. Dashboard page: sidebar, 8 KPI cards, two Recharts charts, tabs with a form and a 10-item list, static cards.
react_compilerreact_compiler+auto_memoize=FalsePython compile time with auto-memo off (from #7272):
Testing
uv run ruff check .,uv run ruff format .,uv run pyright reflex tests(0 errors), anduv run pre-commit run --all-filesall pass. The.pyihook made no changes.tests/units/compiler,tests/units/reflex_base,test_prerequisites.py,test_config.py, andcomponents/test_component.py: 1943 passed.reflex_bench/drivers/test_browser.pytests, because the dev container lacks the Playwright headless-shell binary those tests need. That is unrelated to this diff.react_compiler=True, auto_memoize=Falseunderreflex run --env prod:rx.set_focusthroughuseRegisteredRef, and a controlled-input round trip all worked, with no page errors.Known limits (from #7272)
auto_memoize=False, a page the compiler skips re-renders fully on every state change, about 2× slower than today. The docs say to measure before turning it off.https://claude.ai/code/session_01LwJUCTv3FVWzi1UoUpyMLT
Generated by Claude Code