Repository navigation
React: Keep the act environment set while a story tears down - #36608
valentinpalkovic merged 1 commit into
Conversation
The teardown awaited a sync act callback. withGlobalActEnvironment restores IS_REACT_ACT_ENVIRONMENT before it returns React's thenable, and awaiting that thenable reopens React's act queue. A lazy chunk that resolved then logged "The current testing environment is not configured to support act(...)" in the next story. An async callback keeps the flag set until React finishes. Co-Authored-By: Claude <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 🧰 Additional context used📚 Code guidelines (1)No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review. WalkthroughThe React renderer now awaits ChangesReact renderer teardown
Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to Teardown now waits for React’s async work, and the regression test covers the lazy-resolution warning. No actionable merge-blocking risk remains. ✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Thank you for this fix, @malcolmwhite-surge! All three review stages passed on commit
Two optional suggestions. Neither is required:
Not covered: we did not reproduce the issue in a real browser with a code-split chunk. We did not run type checking, lint, or the full renderer suite on React 19, and we ran the tests on Node 24 instead of the pinned Node 22. A maintainer makes the final decision on merging. Thanks again for the clear test and the careful fix! |
Package BenchmarksCommit: The following packages have significant changes to their size or dependencies:
|
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 1 | 1 | 0 |
| Self size | 206 KB | 195 KB | 🎉 -11 KB 🎉 |
| Dependency size | 28 KB | 28 KB | 0 B |
| Bundle Size Analyzer | Link | Link |
@storybook/builder-webpack5
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 205 | 181 | 🎉 -24 🎉 |
| Self size | 92 KB | 92 KB | 🚨 +240 B 🚨 |
| Dependency size | 46.11 MB | 38.84 MB | 🎉 -7.27 MB 🎉 |
| Bundle Size Analyzer | Link | Link |
storybook
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 73 | 73 | 0 |
| Self size | 21.57 MB | 21.75 MB | 🚨 +178 KB 🚨 |
| Dependency size | 31.24 MB | 31.24 MB | 0 B |
| Bundle Size Analyzer | Link | Link |
@storybook/angular
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 213 | 186 | 🎉 -27 🎉 |
| Self size | 240 KB | 240 KB | 🚨 +51 B 🚨 |
| Dependency size | 39.41 MB | 31.68 MB | 🎉 -7.72 MB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/angular-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 20 | 20 | 0 |
| Self size | 23.42 MB | 23.42 MB | 🚨 +1 KB 🚨 |
| Dependency size | 11.56 MB | 11.55 MB | 🎉 -11 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/ember
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 204 | 180 | 🎉 -24 🎉 |
| Self size | 13 KB | 13 KB | 🚨 +18 B 🚨 |
| Dependency size | 41.43 MB | 34.16 MB | 🎉 -7.27 MB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/html-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 4 | 4 | 0 |
| Self size | 22 KB | 22 KB | 0 B |
| Dependency size | 269 KB | 259 KB | 🎉 -11 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/nextjs
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 469 | 445 | 🎉 -24 🎉 |
| Self size | 641 KB | 641 KB | 🎉 -117 B 🎉 |
| Dependency size | 69.83 MB | 62.56 MB | 🎉 -7.27 MB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/nextjs-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 100 | 100 | 0 |
| Self size | 1.42 MB | 1.42 MB | 🚨 +3 B 🚨 |
| Dependency size | 24.16 MB | 24.14 MB | 🎉 -13 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/preact-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 4 | 4 | 0 |
| Self size | 12 KB | 12 KB | 0 B |
| Dependency size | 287 KB | 276 KB | 🎉 -10 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/react-native-web-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 117 | 120 | 🚨 +3 🚨 |
| Self size | 104 KB | 29 KB | 🎉 -75 KB 🎉 |
| Dependency size | 26.10 MB | 26.28 MB | 🚨 +185 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/react-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 78 | 78 | 0 |
| Self size | 33 KB | 32 KB | 🎉 -609 B 🎉 |
| Dependency size | 21.27 MB | 21.26 MB | 🎉 -12 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/react-webpack5
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 289 | 265 | 🎉 -24 🎉 |
| Self size | 23 KB | 23 KB | 0 B |
| Dependency size | 58.56 MB | 51.29 MB | 🎉 -7.27 MB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/server-webpack5
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 217 | 193 | 🎉 -24 🎉 |
| Self size | 15 KB | 15 KB | 🎉 -12 B 🎉 |
| Dependency size | 47.39 MB | 40.12 MB | 🎉 -7.27 MB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/tanstack-react
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 79 | 79 | 0 |
| Self size | 124 KB | 124 KB | 🎉 -84 B 🎉 |
| Dependency size | 21.31 MB | 21.30 MB | 🎉 -13 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/vue3-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 95 | 95 | 0 |
| Self size | 32 KB | 32 KB | 🚨 +140 B 🚨 |
| Dependency size | 18.77 MB | 18.73 MB | 🎉 -31 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/web-components-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 6 | 6 | 0 |
| Self size | 19 KB | 19 KB | 0 B |
| Dependency size | 515 KB | 423 KB | 🎉 -91 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/cli
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 179 | 179 | 0 |
| Self size | 872 KB | 863 KB | 🎉 -9 KB 🎉 |
| Dependency size | 87.01 MB | 87.19 MB | 🚨 +177 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/codemod
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 172 | 172 | 0 |
| Self size | 44 KB | 44 KB | 0 B |
| Dependency size | 85.50 MB | 85.68 MB | 🚨 +178 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
create-storybook
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 74 | 74 | 0 |
| Self size | 1.07 MB | 1.07 MB | 🎉 -633 B 🎉 |
| Dependency size | 52.81 MB | 52.99 MB | 🚨 +178 KB 🚨 |
| Bundle Size Analyzer | node | node |
@storybook/vue3
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 91 | 91 | 0 |
| Self size | 229 KB | 208 KB | 🎉 -20 KB 🎉 |
| Dependency size | 18.21 MB | 18.21 MB | 0 B |
| Bundle Size Analyzer | Link | Link |
@storybook/web-components
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 4 | 4 | 0 |
| Self size | 182 KB | 101 KB | 🎉 -81 KB 🎉 |
| Dependency size | 126 KB | 126 KB | 0 B |
| Bundle Size Analyzer | Link | Link |
What I did
A story whose
React.lazychunk is still loading when the story is torn down can make the next story fail with:The teardown returned by
renderToCanvascallsawait act(() => { unmountElement(canvasElement); })with a sync callback. For a sync callback,withGlobalActEnvironmentrestoresIS_REACT_ACT_ENVIRONMENTbefore it returns React's thenable. When the teardown awaits that thenable, React reopens its act queue and keeps flushing it while work remains. A lazy chunk that resolves in that window makes React callisConcurrentActEnvironment(), which sees the act queue open and the flag false, so it logs the error. Portable stories run the saved teardown at the start of the next story, so the error lands in the next story's console checks.This PR makes the teardown callback async:
await act(async () => { unmountElement(canvasElement); }). That uses the existing async branch ofwithGlobalActEnvironment, which keeps the flag set until React's thenable settles. The render path in the same file already passes an async callback. The teardown was the only awaited syncactin the renderer.Related: #34708 and #34999 cover a different warning (
act(async)withoutawait). With only #34999 applied, the new test still fails. #30356 shows the same message in docs mode, with a different cause, and #31483 fixed it.Seen with Storybook 10.6.0 and React 19.3.0. The new test fails without the fix on React 18.3.1 and 19.2.8, and passes with it.
Checklist for Contributors
Testing
The changes in this PR are covered in the following automated tests:
Manual testing
yarn nx run-many -t compile, so that every Vitest project config can load.yarn vitest run --project @storybook/react code/renderers/react/src/renderToCanvas.test.tsx. All tests pass.code/renderers/react/src/renderToCanvas.tsx, change the teardown back toawait act(() => {. Run the same command. The test "keeps the act environment enabled while a lazy chunk resolves during teardown" fails, because "The current testing environment is not configured to support act(...)" was logged.Documentation
MIGRATION.MD
Checklist for Maintainers
When this PR is ready for testing, make sure to add
ci:normal,ci:mergedorci:dailyGH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found incode/lib/cli-storybook/src/sandbox-templates.tsDeclare whether manual QA will be needed for this PR during the next release, through
qa:neededorqa:skipMake sure this PR contains one of the labels below:
Available labels
bug: Internal changes that fixes incorrect behavior.maintenance: User-facing maintenance tasks.dependencies: Upgrading (sometimes downgrading) dependencies.build: Internal-facing build tooling & test updates. Will not show up in release changelog.cleanup: Minor cleanup style change. Will not show up in release changelog.documentation: Documentation only changes. Will not show up in release changelog.feature request: Introducing a new feature.BREAKING CHANGE: Changes that break compatibility in some way with current major version.other: Changes that don't fit in the above categories.AI assistance: I prepared this change with Claude Code, then reviewed the fix and the test myself.