Skip to content

Conversation

@sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Mar 26, 2025

Starting a View Transition is an async sequence. Since React can get a sync update in the middle of sequence we sometimes interrupt that sequence.

Currently, we don't actually cancel the View Transition so it can just run as a partial. This ensures that we fully skip it when that happens, as well as warn.

However, it's very easy to trigger this with just a setState in useLayoutEffect right now. Therefore if we're inside the preparing sequence of a startViewTransition, this delays work that would've normally flushed in a microtask. Maybe we want to do the same for Default work already scheduled through a scheduler Task. Edit: This was already done.

flushSync currently will still lead to an interrupted View Transition (with a warning). There's a tradeoff here whether we want to try our best to preserve the guarantees of flushSync or favor the animation. It's already possible to suspend at the root with flushSync which means it's not always 100% guaranteed to commit anyway. We could treat it as suspended. But let's see how much this is a problem in practice.

@sebmarkbage sebmarkbage requested a review from acdlite March 26, 2025 18:14
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Mar 26, 2025
@react-sizebot
Copy link

react-sizebot commented Mar 26, 2025

Comparing: 5f232d7...0da7164

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB = 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js +0.05% 514.93 kB 515.21 kB = 91.73 kB 91.74 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB +0.05% 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js +0.05% 614.48 kB 614.76 kB +0.07% 108.73 kB 108.81 kB
facebook-www/ReactDOM-prod.classic.js +0.70% 646.21 kB 650.72 kB +0.56% 114.22 kB 114.86 kB
facebook-www/ReactDOM-prod.modern.js +0.71% 636.49 kB 641.00 kB +0.58% 112.65 kB 113.29 kB
facebook-react-native/react/cjs/React-prod.js +2.91% 19.17 kB 19.72 kB +3.01% 4.98 kB 5.13 kB
facebook-react-native/react/cjs/React-profiling.js +2.84% 19.60 kB 20.16 kB +2.94% 5.06 kB 5.21 kB
facebook-www/React-prod.modern.js +2.52% 20.51 kB 21.03 kB +2.38% 5.25 kB 5.38 kB
facebook-www/React-prod.classic.js +2.52% 20.51 kB 21.03 kB +2.38% 5.25 kB 5.38 kB
facebook-www/React-profiling.modern.js +2.46% 20.95 kB 21.46 kB +2.36% 5.34 kB 5.46 kB
facebook-www/React-profiling.classic.js +2.46% 20.95 kB 21.46 kB +2.36% 5.34 kB 5.46 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
facebook-react-native/react/cjs/React-prod.js +2.91% 19.17 kB 19.72 kB +3.01% 4.98 kB 5.13 kB
facebook-react-native/react/cjs/React-profiling.js +2.84% 19.60 kB 20.16 kB +2.94% 5.06 kB 5.21 kB
facebook-www/React-prod.modern.js +2.52% 20.51 kB 21.03 kB +2.38% 5.25 kB 5.38 kB
facebook-www/React-prod.classic.js +2.52% 20.51 kB 21.03 kB +2.38% 5.25 kB 5.38 kB
facebook-www/React-profiling.modern.js +2.46% 20.95 kB 21.46 kB +2.36% 5.34 kB 5.46 kB
facebook-www/React-profiling.classic.js +2.46% 20.95 kB 21.46 kB +2.36% 5.34 kB 5.46 kB
react-native/implementations/ReactFabric-dev.fb.js +1.84% 658.50 kB 670.58 kB +1.47% 107.57 kB 109.15 kB
react-native/implementations/ReactNativeRenderer-dev.fb.js +1.82% 663.43 kB 675.52 kB +1.49% 108.42 kB 110.03 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-dev.js +1.74% 588.40 kB 598.62 kB +1.42% 95.15 kB 96.51 kB
facebook-www/ReactART-dev.modern.js +1.57% 687.86 kB 698.67 kB +1.25% 108.09 kB 109.43 kB
facebook-www/ReactART-dev.classic.js +1.55% 697.36 kB 708.16 kB +1.23% 109.92 kB 111.27 kB
facebook-react-native/react/cjs/React-dev.js +1.37% 51.03 kB 51.73 kB +1.24% 11.35 kB 11.49 kB
facebook-www/React-dev.modern.js +1.36% 54.82 kB 55.56 kB +1.21% 11.97 kB 12.11 kB
facebook-www/React-dev.classic.js +1.36% 54.82 kB 55.56 kB +1.21% 11.97 kB 12.11 kB
facebook-www/ReactReconciler-dev.modern.js +1.34% 795.82 kB 806.51 kB +0.97% 124.88 kB 126.10 kB
facebook-www/ReactReconciler-dev.classic.js +1.33% 805.03 kB 815.71 kB +0.97% 126.53 kB 127.75 kB
react-native/implementations/ReactFabric-prod.fb.js +1.29% 375.51 kB 380.37 kB +1.25% 65.26 kB 66.08 kB
react-native/implementations/ReactNativeRenderer-prod.fb.js +1.28% 379.15 kB 384.01 kB +1.18% 65.95 kB 66.73 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js +1.24% 982.20 kB 994.34 kB +0.90% 165.36 kB 166.85 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js +1.22% 998.52 kB 1,010.67 kB +0.88% 168.19 kB 169.67 kB
react-native/implementations/ReactFabric-profiling.fb.js +1.20% 401.01 kB 405.83 kB +1.18% 69.18 kB 69.99 kB
react-native/implementations/ReactNativeRenderer-profiling.fb.js +1.19% 404.65 kB 409.47 kB +1.13% 69.80 kB 70.59 kB
facebook-www/ReactART-prod.modern.js +1.19% 369.01 kB 373.40 kB +1.08% 62.18 kB 62.85 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-prod.js +1.18% 329.68 kB 333.58 kB +1.05% 57.62 kB 58.23 kB
facebook-www/ReactART-prod.classic.js +1.16% 379.00 kB 383.38 kB +1.04% 63.81 kB 64.48 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-profiling.js +1.11% 351.81 kB 355.71 kB +1.03% 60.80 kB 61.43 kB
oss-stable-semver/react/cjs/react.production.js +1.08% 16.89 kB 17.08 kB +1.62% 4.38 kB 4.46 kB
oss-stable/react/cjs/react.production.js +1.08% 16.92 kB 17.10 kB +1.61% 4.41 kB 4.48 kB
oss-experimental/react/cjs/react.production.js +0.99% 18.36 kB 18.54 kB +1.42% 4.73 kB 4.80 kB
facebook-www/ReactReconciler-prod.modern.js +0.98% 482.55 kB 487.30 kB +0.89% 77.54 kB 78.23 kB
facebook-www/ReactReconciler-prod.classic.js +0.96% 492.86 kB 497.61 kB +0.86% 79.17 kB 79.86 kB
facebook-www/ReactDOM-dev.modern.js +0.93% 1,157.06 kB 1,167.87 kB +0.69% 192.57 kB 193.89 kB
facebook-www/ReactDOM-dev.classic.js +0.93% 1,166.20 kB 1,177.01 kB +0.69% 194.25 kB 195.60 kB
facebook-www/ReactDOMTesting-dev.modern.js +0.92% 1,173.60 kB 1,184.41 kB +0.69% 196.31 kB 197.67 kB
facebook-www/ReactDOMTesting-dev.classic.js +0.91% 1,182.74 kB 1,193.55 kB +0.69% 198.06 kB 199.41 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js +0.86% 538.61 kB 543.24 kB +0.77% 95.62 kB 96.35 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js +0.85% 544.12 kB 548.74 kB +0.76% 96.70 kB 97.43 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js +0.81% 563.44 kB 568.03 kB +0.72% 99.30 kB 100.02 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js +0.81% 569.38 kB 573.97 kB +0.71% 100.46 kB 101.18 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer.production.js +0.76% 37.83 kB 38.12 kB +0.46% 7.02 kB 7.05 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer.production.js +0.76% 37.86 kB 38.15 kB +0.45% 7.05 kB 7.08 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer.production.js +0.76% 37.86 kB 38.15 kB +0.44% 7.06 kB 7.09 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-persistent.production.js +0.76% 37.96 kB 38.25 kB +0.45% 7.04 kB 7.07 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-persistent.production.js +0.76% 37.99 kB 38.27 kB +0.45% 7.07 kB 7.10 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-persistent.production.js +0.76% 37.99 kB 38.28 kB +0.45% 7.08 kB 7.11 kB
test_utils/ReactAllWarnings.js +0.76% 63.72 kB 64.20 kB +0.97% 15.96 kB 16.11 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer.development.js +0.73% 42.11 kB 42.42 kB +0.46% 7.62 kB 7.66 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer.development.js +0.73% 42.13 kB 42.44 kB +0.46% 7.66 kB 7.69 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer.development.js +0.73% 42.14 kB 42.45 kB +0.46% 7.66 kB 7.69 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-persistent.development.js +0.73% 42.25 kB 42.56 kB +0.46% 7.64 kB 7.68 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-persistent.development.js +0.73% 42.27 kB 42.58 kB +0.44% 7.67 kB 7.71 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-persistent.development.js +0.73% 42.28 kB 42.59 kB +0.44% 7.68 kB 7.71 kB
facebook-www/ReactDOM-prod.modern.js +0.71% 636.49 kB 641.00 kB +0.58% 112.65 kB 113.29 kB
facebook-www/ReactDOM-prod.classic.js +0.70% 646.21 kB 650.72 kB +0.56% 114.22 kB 114.86 kB
facebook-www/ReactDOMTesting-prod.modern.js +0.69% 650.89 kB 655.40 kB +0.56% 116.25 kB 116.90 kB
facebook-www/ReactDOMTesting-prod.classic.js +0.68% 660.61 kB 665.12 kB +0.55% 117.85 kB 118.50 kB
facebook-www/ReactDOM-profiling.modern.js +0.68% 700.83 kB 705.58 kB +0.59% 121.91 kB 122.63 kB
facebook-www/ReactDOM-profiling.classic.js +0.67% 708.87 kB 713.62 kB +0.59% 123.25 kB 123.98 kB
facebook-react-native/react/cjs/JSXRuntime-dev.js +0.59% 12.47 kB 12.54 kB +0.12% 3.35 kB 3.35 kB
facebook-www/JSXDEVRuntime-dev.classic.js +0.55% 13.37 kB 13.44 kB +0.22% 3.60 kB 3.61 kB
facebook-www/JSXDEVRuntime-dev.modern.js +0.55% 13.37 kB 13.44 kB +0.22% 3.60 kB 3.61 kB
facebook-react-native/react/cjs/JSXDEVRuntime-dev.js +0.51% 12.07 kB 12.13 kB +0.12% 3.33 kB 3.33 kB
oss-stable-semver/react/cjs/react.development.js +0.45% 44.83 kB 45.03 kB +0.54% 10.23 kB 10.28 kB
oss-stable/react/cjs/react.development.js +0.45% 44.85 kB 45.06 kB +0.53% 10.26 kB 10.31 kB
oss-experimental/react/cjs/react.development.js +0.43% 46.55 kB 46.75 kB +0.53% 10.62 kB 10.67 kB

Generated by 🚫 dangerJS against 0da7164

This unifies the pattern with startGestureTransition that already did this
so that we can cancel it if needed.
This ensures that we don't play a partial animation when we're interrupted.
We already had an equivalent helper that we already used in root scheduler.
@sebmarkbage
Copy link
Collaborator Author

@jherr I believe this will fix the issue you found. Doesn't have to do with the heuristics but rather the tradeoff between synchronous execution vs View Transitions.

@sebmarkbage sebmarkbage merged commit a5297ec into facebook:main Mar 26, 2025
241 checks passed
github-actions bot pushed a commit that referenced this pull request Mar 26, 2025
…on async sequence (#32760)

Starting a View Transition is an async sequence. Since React can get a
sync update in the middle of sequence we sometimes interrupt that
sequence.

Currently, we don't actually cancel the View Transition so it can just
run as a partial. This ensures that we fully skip it when that happens,
as well as warn.

However, it's very easy to trigger this with just a setState in
useLayoutEffect right now. Therefore if we're inside the preparing
sequence of a startViewTransition, this delays work that would've
normally flushed in a microtask. ~Maybe we want to do the same for
Default work already scheduled through a scheduler Task.~ Edit: This was
already done.

`flushSync` currently will still lead to an interrupted View Transition
(with a warning). There's a tradeoff here whether we want to try our
best to preserve the guarantees of `flushSync` or favor the animation.
It's already possible to suspend at the root with `flushSync` which
means it's not always 100% guaranteed to commit anyway. We could treat
it as suspended. But let's see how much this is a problem in practice.

DiffTrain build for [a5297ec](a5297ec)
github-actions bot pushed a commit that referenced this pull request Mar 26, 2025
…on async sequence (#32760)

Starting a View Transition is an async sequence. Since React can get a
sync update in the middle of sequence we sometimes interrupt that
sequence.

Currently, we don't actually cancel the View Transition so it can just
run as a partial. This ensures that we fully skip it when that happens,
as well as warn.

However, it's very easy to trigger this with just a setState in
useLayoutEffect right now. Therefore if we're inside the preparing
sequence of a startViewTransition, this delays work that would've
normally flushed in a microtask. ~Maybe we want to do the same for
Default work already scheduled through a scheduler Task.~ Edit: This was
already done.

`flushSync` currently will still lead to an interrupted View Transition
(with a warning). There's a tradeoff here whether we want to try our
best to preserve the guarantees of `flushSync` or favor the animation.
It's already possible to suspend at the root with `flushSync` which
means it's not always 100% guaranteed to commit anyway. We could treat
it as suspended. But let's see how much this is a problem in practice.

DiffTrain build for [a5297ec](a5297ec)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants