From d74f061b6908e4841b2eb09c296ca4658dbdd38e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Wed, 1 Oct 2025 21:58:13 -0400 Subject: [PATCH] [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. --- .../src/client/ReactFiberConfigDOM.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 7d28dc43ca..0f75d5f8cd 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -2279,6 +2279,11 @@ export function startViewTransition( spawnedWorkCallback(); }; const handleError = (error: mixed) => { + // $FlowFixMe[prop-missing] + if (ownerDocument.__reactViewTransition === transition) { + // $FlowFixMe[prop-missing] + ownerDocument.__reactViewTransition = null; + } try { error = customizeViewTransitionError(error, false); if (error !== null) { @@ -2293,6 +2298,9 @@ export function startViewTransition( layoutCallback(); // Skip afterMutationCallback() since we're not animating. spawnedWorkCallback(); + if (enableProfilerTimer) { + finishedAnimation(); + } } }; transition.ready.then(readyCallback, handleError); @@ -2699,6 +2707,11 @@ export function startGestureTransition( ? () => requestAnimationFrame(readyCallback) : readyCallback; const handleError = (error: mixed) => { + // $FlowFixMe[prop-missing] + if (ownerDocument.__reactViewTransition === transition) { + // $FlowFixMe[prop-missing] + ownerDocument.__reactViewTransition = null; + } try { error = customizeViewTransitionError(error, true); if (error !== null) { @@ -2713,6 +2726,9 @@ export function startGestureTransition( // Skip readyCallback() and go straight to animateCallbck() since we're not animating. // animateCallback() is still required to restore states. animateCallback(); + if (enableProfilerTimer) { + finishedAnimation(); + } } }; transition.ready.then(readyForAnimations, handleError);