[Fiber] Clean up ViewTransition when it fails to start (#34676)
The View Transition docs were unclear about this but apparently the `finished` promise never settles if the animation never started. So if there's an error that rejects the `ready` promise, we'll never run the clean up which can cause it to stall. Fixes #34662. However, ultimately that is caused by Chrome stalling our default `onDefaultTransitionIndicator` but it should be unblocked after 10 seconds, not a minute.
Sebastian Markbåge committed
Oct 1, 2025 at 21:58 UTC
d74f061b6908e4841b2eb09c296ca4658dbdd38e
1 file changed
+16
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+16
@@ -2279,6 +2279,11 @@ export function startViewTransition(
2279
spawnedWorkCallback();
2280
};
2281
const handleError = (error: mixed) => {
2282
+ // $FlowFixMe[prop-missing]
2283
+ if (ownerDocument.__reactViewTransition === transition) {
2284
+ // $FlowFixMe[prop-missing]
2285
+ ownerDocument.__reactViewTransition = null;
2286
+ }
2287
try {
2288
error = customizeViewTransitionError(error, false);
2289
if (error !== null) {
@@ -2293,6 +2298,9 @@ export function startViewTransition(
2298
layoutCallback();
2299
// Skip afterMutationCallback() since we're not animating.
2300
spawnedWorkCallback();
2301
+ if (enableProfilerTimer) {
2302
+ finishedAnimation();
2303
+ }
2304
}
2305
};
2306
transition.ready.then(readyCallback, handleError);
@@ -2699,6 +2707,11 @@ export function startGestureTransition(
2707
? () => requestAnimationFrame(readyCallback)
2708
: readyCallback;
2709
const handleError = (error: mixed) => {
2710
+ // $FlowFixMe[prop-missing]
2711
+ if (ownerDocument.__reactViewTransition === transition) {
2712
+ // $FlowFixMe[prop-missing]
2713
+ ownerDocument.__reactViewTransition = null;
2714
+ }
2715
try {
2716
error = customizeViewTransitionError(error, true);
2717
if (error !== null) {
@@ -2713,6 +2726,9 @@ export function startGestureTransition(
2726
// Skip readyCallback() and go straight to animateCallbck() since we're not animating.
2727
// animateCallback() is still required to restore states.
2728
animateCallback();
2729
+ if (enableProfilerTimer) {
2730
+ finishedAnimation();
2731
+ }
2732
}
2733
};
2734
transition.ready.then(readyForAnimations, handleError);