@samitouri / QOS-React / commits / d74f061b69

[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);