@@ -1065,6 +1065,7 @@ export function attach(
10651065 setErrorHandler,
10661066 setSuspenseHandler,
10671067 scheduleUpdate,
1068+ scheduleRetry,
10681069 getCurrentFiber,
10691070 } = renderer;
10701071 const supportsTogglingError =
@@ -7754,7 +7755,13 @@ export function attach(
77547755 // First override is added. Switch React to slower path.
77557756 setErrorHandler(shouldErrorFiberAccordingToMap);
77567757 }
7757- scheduleUpdate(fiber);
7758+ if (!forceError && typeof scheduleRetry === 'function') {
7759+ // If we're dismissing an error and the renderer supports it, use a Retry instead of Sync
7760+ // This would allow View Transitions to proceed as if the error was dismissed using a Transition.
7761+ scheduleRetry(fiber);
7762+ } else {
7763+ scheduleUpdate(fiber);
7764+ }
77587765 }
77597766
77607767 function shouldSuspendFiberAlwaysFalse() {
@@ -7812,7 +7819,13 @@ export function attach(
78127819 setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
78137820 }
78147821 }
7815- scheduleUpdate(fiber);
7822+ if (!forceFallback && typeof scheduleRetry === 'function') {
7823+ // If we're unsuspending and the renderer supports it, use a Retry instead of Sync
7824+ // to allow for things like View Transitions to proceed the way they would for real.
7825+ scheduleRetry(fiber);
7826+ } else {
7827+ scheduleUpdate(fiber);
7828+ }
78167829 }
78177830
78187831 /**
@@ -7834,11 +7847,10 @@ export function attach(
78347847 }
78357848
78367849 // TODO: Allow overriding the timeline for the specified root.
7837- forceFallbackForFibers.forEach(fiber => {
7838- scheduleUpdate(fiber);
7839- });
7840- forceFallbackForFibers.clear();
78417850
7851+ const unsuspendedSet: Set<Fiber> = new Set(forceFallbackForFibers);
7852+
7853+ let resuspended = false;
78427854 for (let i = 0; i < suspendedSet.length; ++i) {
78437855 const instance = idToDevToolsInstanceMap.get(suspendedSet[i]);
78447856 if (instance === undefined) {
@@ -7850,15 +7862,41 @@ export function attach(
78507862
78517863 if ( instance . kind === FIBER_INSTANCE ) {
78527864 const fiber = instance . data ;
7853- forceFallbackForFibers . add ( fiber ) ;
7854- // We could find a minimal set that covers all the Fibers in this suspended set.
7855- // For now we rely on React's batching of updates.
7856- scheduleUpdate ( fiber ) ;
7865+ if (
7866+ forceFallbackForFibers . has ( fiber ) ||
7867+ ( fiber . alternate !== null &&
7868+ forceFallbackForFibers . has ( fiber . alternate ) )
7869+ ) {
7870+ // We're already forcing fallback for this fiber. Mark it as not unsuspended.
7871+ unsuspendedSet . delete ( fiber ) ;
7872+ if ( fiber . alternate !== null ) {
7873+ unsuspendedSet . delete ( fiber . alternate ) ;
7874+ }
7875+ } else {
7876+ forceFallbackForFibers . add ( fiber ) ;
7877+ // We could find a minimal set that covers all the Fibers in this suspended set.
7878+ // For now we rely on React's batching of updates.
7879+ scheduleUpdate ( fiber ) ;
7880+ resuspended = true ;
7881+ }
78577882 } else {
78587883 console . warn ( `Cannot not suspend ID '${ suspendedSet [ i ] } '.` ) ;
78597884 }
78607885 }
78617886
7887+ // Unsuspend any existing forced fallbacks if they're not in the new set.
7888+ unsuspendedSet . forEach ( fiber => {
7889+ forceFallbackForFibers . delete ( fiber ) ;
7890+ if ( ! resuspended && typeof scheduleRetry === 'function' ) {
7891+ // If nothing new resuspended we don't need this to be sync. If we're only
7892+ // unsuspending then we can schedule this as a Retry if the renderer supports it.
7893+ // That way we can trigger animations.
7894+ scheduleRetry ( fiber ) ;
7895+ } else {
7896+ scheduleUpdate ( fiber ) ;
7897+ }
7898+ } ) ;
7899+
78627900 if ( forceFallbackForFibers . size > 0 ) {
78637901 // First override is added. Switch React to slower path.
78647902 // TODO: Semantics for suspending a timeline are different. We want a suspended
0 commit comments