@@ -22,7 +22,10 @@ import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
2222import type { UpdateQueue } from './ReactUpdateQueue.old' ;
2323import type { FunctionComponentUpdateQueue } from './ReactFiberHooks.old' ;
2424import type { Wakeable } from 'shared/ReactTypes' ;
25- import type { OffscreenState } from './ReactFiberOffscreenComponent' ;
25+ import type {
26+ OffscreenState ,
27+ OffscreenInstance ,
28+ } from './ReactFiberOffscreenComponent' ;
2629import type { HookFlags } from './ReactHookEffectTags' ;
2730import type { Cache } from './ReactFiberCacheComponent.old' ;
2831import type { RootState } from './ReactFiberRoot.old' ;
@@ -62,6 +65,7 @@ import {
6265 OffscreenComponent ,
6366 LegacyHiddenComponent ,
6467 CacheComponent ,
68+ TracingMarkerComponent ,
6569} from './ReactWorkTags' ;
6670import { detachDeletedInstance } from './ReactFiberHostConfig' ;
6771import {
@@ -1001,7 +1005,8 @@ function commitLayoutEffectOnFiber(
10011005 case IncompleteClassComponent:
10021006 case ScopeComponent:
10031007 case OffscreenComponent:
1004- case LegacyHiddenComponent: {
1008+ case LegacyHiddenComponent:
1009+ case TracingMarkerComponent: {
10051010 break ;
10061011 }
10071012
@@ -1066,6 +1071,77 @@ function reappearLayoutEffectsOnFiber(node: Fiber) {
10661071 }
10671072}
10681073
1074+ function commitTransitionProgress (
1075+ finishedRoot : FiberRoot ,
1076+ offscreenFiber : Fiber ,
1077+ ) {
1078+ // This function adds suspense boundaries to the root
1079+ // or tracing marker's pendingSuspenseBoundaries map.
1080+ // When a suspense boundary goes from a resolved to a fallback
1081+ // state we add the boundary to the map, and when it goes from
1082+ // a fallback to a resolved state, we remove the boundary from
1083+ // the map.
1084+
1085+ // We use stateNode on the Offscreen component as a stable object
1086+ // that doesnt change from render to render. This way we can
1087+ // distinguish between different Offscreen instances (vs. the same
1088+ // Offscreen instance with different fibers)
1089+ const offscreenInstance : OffscreenInstance = offscreenFiber . stateNode ;
1090+
1091+ let prevState : SuspenseState | null = null ;
1092+ const previousFiber = offscreenFiber . alternate ;
1093+ if ( previousFiber !== null && previousFiber . memoizedState !== null ) {
1094+ prevState = previousFiber . memoizedState ;
1095+ }
1096+ const nextState : SuspenseState | null = offscreenFiber . memoizedState ;
1097+
1098+ const wasHidden = prevState !== null ;
1099+ const isHidden = nextState !== null ;
1100+
1101+ const rootState : RootState = finishedRoot . current . memoizedState ;
1102+ // TODO(luna) move pendingSuspenseBoundaries and transitions from
1103+ // HostRoot fiber to FiberRoot
1104+ const rootPendingBoundaries = rootState . pendingSuspenseBoundaries ;
1105+
1106+ // If there is a name on the suspense boundary, store that in
1107+ // the pending boundaries.
1108+ let name = null ;
1109+ const parent = offscreenFiber . return ;
1110+ if (
1111+ parent !== null &&
1112+ parent . tag === SuspenseComponent &&
1113+ parent . memoizedProps . unstable_name
1114+ ) {
1115+ name = parent . memoizedProps . unstable_name ;
1116+ }
1117+
1118+ if ( rootPendingBoundaries !== null ) {
1119+ if ( previousFiber === null ) {
1120+ // Initial mount
1121+ if ( isHidden ) {
1122+ rootPendingBoundaries . set ( offscreenInstance , {
1123+ name,
1124+ } ) ;
1125+ }
1126+ } else {
1127+ if ( wasHidden && ! isHidden ) {
1128+ // The suspense boundary went from hidden to visible. Remove
1129+ // the boundary from the pending suspense boundaries set
1130+ // if it's there
1131+ if ( rootPendingBoundaries . has ( offscreenInstance ) ) {
1132+ rootPendingBoundaries . delete ( offscreenInstance ) ;
1133+ }
1134+ } else if ( ! wasHidden && isHidden ) {
1135+ // The suspense boundaries was just hidden. Add the boundary
1136+ // to the pending boundary set if it's there
1137+ rootPendingBoundaries . set ( offscreenInstance , {
1138+ name,
1139+ } ) ;
1140+ }
1141+ }
1142+ }
1143+ }
1144+
10691145function hideOrUnhideAllChildren ( finishedWork , isHidden ) {
10701146 // Only hide or unhide the top-most host nodes.
10711147 let hostSubtreeRoot = null ;
@@ -2747,22 +2823,49 @@ function commitPassiveMountOnFiber(
27472823 }
27482824
27492825 if ( enableTransitionTracing ) {
2750- if ( committedTransitions !== null ) {
2826+ // Get the transitions that were initiatized during the render
2827+ // and add a start transition callback for each of them
2828+ const state = finishedWork . memoizedState ;
2829+ if ( state . transitions === null ) {
2830+ state . transitions = new Set ( [ ] ) ;
2831+ }
2832+ const pendingTransitions = state . transitions ;
2833+
2834+ if ( committedTransitions != null ) {
27512835 committedTransitions . forEach ( transition => {
2752- // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
27532836 addTransitionStartCallbackToPendingTransition ( {
27542837 transitionName : transition . name ,
27552838 startTime : transition . startTime ,
27562839 } ) ;
2840+ pendingTransitions . add ( transition ) ;
2841+ } ) ;
27572842
2843+ clearTransitionsForLanes ( finishedRoot , committedLanes ) ;
2844+ }
2845+
2846+ const pendingSuspenseBoundaries = state . pendingSuspenseBoundaries ;
2847+ // process the lazy transitions list by filtering duplicate transitions
2848+ // and calling the transition complete callback on all transitions
2849+ // if there are no more pending suspense boundaries
2850+ pendingTransitions . forEach ( transition => {
2851+ if (
2852+ pendingSuspenseBoundaries === null ||
2853+ pendingSuspenseBoundaries . size === 0
2854+ ) {
27582855 addTransitionCompleteCallbackToPendingTransition ( {
27592856 transitionName : transition . name ,
27602857 startTime : transition . startTime ,
27612858 } ) ;
2762- } ) ;
2859+ }
2860+ } ) ;
27632861
2764- clearTransitionsForLanes ( finishedRoot , committedLanes ) ;
2765- finishedWork . memoizedState . transitions = null ;
2862+ // If there are no more pending suspense boundaries we
2863+ // clear the transitions because they are all complete.
2864+ if (
2865+ pendingSuspenseBoundaries === null ||
2866+ pendingSuspenseBoundaries . size === 0
2867+ ) {
2868+ state . transitions = null ;
27662869 }
27672870 }
27682871 break ;
@@ -2800,9 +2903,44 @@ function commitPassiveMountOnFiber(
28002903 }
28012904
28022905 if ( enableTransitionTracing ) {
2803- // TODO: Add code to actually process the update queue
2906+ const isFallback = finishedWork . memoizedState ;
2907+ const queue = ( finishedWork . updateQueue : any ) ;
2908+ const rootMemoizedState = finishedRoot . current . memoizedState ;
2909+
2910+ if ( queue !== null ) {
2911+ // We have one instance of the pendingSuspenseBoundaries map.
2912+ // We only need one because we update it during the commit phase.
2913+ // We instantiate a new Map if we haven't already
2914+ if ( rootMemoizedState . pendingSuspenseBoundaries === null ) {
2915+ rootMemoizedState . pendingSuspenseBoundaries = new Map ( ) ;
2916+ }
2917+
2918+ if ( isFallback ) {
2919+ const transitions = queue . transitions ;
2920+ let prevTransitions = finishedWork . memoizedState . transitions ;
2921+ // Add all the transitions saved in the update queue during
2922+ // the render phase (ie the transitions associated with this boundary)
2923+ // into the transitions set.
2924+ if ( transitions != null ) {
2925+ if ( prevTransitions === null ) {
2926+ // We only have one instance of the transitions set
2927+ // because we update it only during the commit phase. We
2928+ // will create the set on a as needed basis in the commit phase
2929+ finishedWork . memoizedState . transitions = prevTransitions = new Set ( ) ;
2930+ }
2931+
2932+ transitions . forEach ( transition => {
2933+ prevTransitions . add ( transition ) ;
2934+ } ) ;
2935+ }
2936+ }
2937+ }
2938+
2939+ commitTransitionProgress ( finishedRoot , finishedWork ) ;
2940+
28042941 finishedWork . updateQueue = null ;
28052942 }
2943+
28062944 break ;
28072945 }
28082946 case CacheComponent : {
0 commit comments