refactor(studio): compile the first eleven hooks under the React Compiler - #3783
Draft
miguel-heygen wants to merge 1 commit into
Draft
Conversation
Each of these hooks was emitted uncompiled because it did something the React Compiler declines: wrote a ref during render, held a try/finally or a throw inside try/catch, carried a react-hooks suppression, or used a lowering the compiler does not support. None of that is visible at runtime, so every memo in them was silently lost. Each is fixed where the value is read, not by changing what happens: ref writes move into an effect or drop when the ref was standing in for a memo, guarded control flow moves to a plain function or a promise handler, and one suppressed dependency list is made true rather than silenced. The bail-out baseline falls from 126 to 115, one per file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands unit C2a, first half (hooks) of the Studio React Compiler coverage plan. Stacked on the ratchet PR (#3773). Ratchet 126 to 115. Also closes a pre-existing floating promise in the block-install handlers.
What
Eleven hooks under
packages/studio/src/hooks,timelineAudioGroupCreate.tsthroughuseElementLifecycleOps.tsin path order, now compile under the React Compiler. The per-file bail-out baseline drops from 126 to 115, one entry per file.It also fixes a defect found while testing one of them: a failed block install left its rejection unhandled, so the user saw an "Adding…" toast and then nothing.
Five new test files pin the paths whose structure changed.
This is the first of two stacked branches. The second takes the remaining eleven hooks.
Why
react({ compiler: true })is silent about what it declines. A hook the compiler skips is emitted exactly as written, so losing every memo inside it costs nothing you can see in a diff, a build log, or a test run. These eleven hooks were all being skipped, and none of it was visible.How
One row per file: the cause the scan reported, the shape of the fix, and the test that holds it.
timelineAudioGroupCreate.tsthrowinsidetry/catchuseAudioGroupCarveAssignment.test.tsx"rejects, and toasts, when an id resolves to no clip"useAnimatedPropertyCommit.tsthrowinsidetry/catch.catch()on its promiseuseAnimatedPropertyCommit.test.tsx"rethrows a persistence failure to the telemetry wrapper"useAppHotkeys.tsuseAppHotkeys.test.ts+.textEditing+.previewForwarding(14 cases)useBlockCatalog.tstry/finallyin the load effecttryuseBlockCatalog.test.tsx(3 cases: loaded, rejected, not-ok)useBlockHandlers.tsreact-hookssuppression;try/finallyfinallymoves to a module functionuseBlockHandlers.test.tsx(4 cases, see the defect note below)useClipboard.tsuseClipboard.test.tsx(paste uses the current project; no project, no read)useConsoleErrorCapture.tsuseConsoleErrorCapture.test.tsx(4 cases incl. restore on unmount)useDomEditPreviewSync.tsuseDomEditPreviewSync.test.tsx(reveal fires; newest callback wins)useDomEditSession.ts??=??instead; the compiler cannot lower a logical assignmentuseDomEditSession.test.tsx,.membersForDeleteuseDomSelection.tsuseDomSelection.test.ts,useDomSelectionSelectionGuards.test.tsuseElementLifecycleOps.tsthrows intry/catch;try/finally.catch()/.finally()on their promisesuseElementLifecycleOps.test.tsx,.multiDelete(21 cases)The block-install defect
Writing the
useBlockHandlerstest surfaced a real one. All four block handlers drop the install promise: three callrunBlockInstallasvoid, and the fourth is awaited from a JSX handler that is itself unawaited. A rejection therefore reached nothing that could report it, and the user was left with "Adding …" and silence.Every caller routes through
runBlockInstall, so the report goes there rather than into four call sites, on the same toast surface the install itself already uses. It resolves tonull, which each caller already reads as "nothing installed". Two new cases cover it, and both fail without the catch.Reachability, stated honestly:
addBlockToProjectcurrently catches everything internally and returnsnull, so no production path reaches the rejection today. The floating promise is the defect; this closes it at the one place all four handlers pass through, so it stays closed if that installer ever throws.Two notes on fix shapes, measured against this toolchain
useLatestRefhelper does not work. The compiler recognises a ref by theuseRefcall site, not by the shape of what a helper returns, so a hook reading.currentfrom a helper's return insideuseCallback([])loses its memoization instead of gaining it. The write stays inline in each hook..catch()and.finally()on a promise are fine wheretry/catchandtry/finallystatements are not, which keeps several of these fixes inside their hooks instead of moving 200 lines to module scope.Test plan
bunx vitest run src/styles/compilerBailouts.test.ts --maxWorkers=4, 14 passed, baseline 126 down to 115, exactly these eleven entries removed.bunx vitest run --maxWorkers=4, 433 passed, 1 skipped (434 files); 4772 passed, 18 todo. The base branch is 428 files and 4753 tests; this adds 5 files and 19 cases.bun run typecheck, clean.oxlintandoxfmt --checkon the 16 changed source files, from the repo root, clean.fallow audit --base origin/main --fail-on-issues, passes. One complexity exemption moves with the code it was already granted for; the remaining finding is inherited, in a function this branch does not touch.finallyor catch was watched failing with that clause broken on purpose.Not covered
useDomEditSession.tsappears in both halves: the??=fix here, one dead argument removed in the next branch. Nothing else in that file changes.