@@ -22,7 +22,10 @@ import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
2222import type { UpdateQueue } from './ReactUpdateQueue.new' ;
2323import type { FunctionComponentUpdateQueue } from './ReactFiberHooks.new' ;
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.new' ;
2831import type { RootState } from './ReactFiberRoot.new' ;
@@ -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,89 @@ function reappearLayoutEffectsOnFiber(node: Fiber) {
10661071 }
10671072}
10681073
1074+ function commitTransitionProgress (
1075+ finishedRoot : FiberRoot ,
1076+ offscreenFiber : Fiber ,
1077+ ) {
1078+ if ( enableTransitionTracing ) {
1079+ // This function adds suspense boundaries to the root
1080+ // or tracing marker's pendingSuspenseBoundaries map.
1081+ // When a suspense boundary goes from a resolved to a fallback
1082+ // state we add the boundary to the map, and when it goes from
1083+ // a fallback to a resolved state, we remove the boundary from
1084+ // the map.
1085+
1086+ // We use stateNode on the Offscreen component as a stable object
1087+ // that doesnt change from render to render. This way we can
1088+ // distinguish between different Offscreen instances (vs. the same
1089+ // Offscreen instance with different fibers)
1090+ const offscreenInstance : OffscreenInstance = offscreenFiber . stateNode ;
1091+
1092+ let prevState : SuspenseState | null = null ;
1093+ const previousFiber = offscreenFiber . alternate ;
1094+ if ( previousFiber !== null && previousFiber . memoizedState !== null ) {
1095+ prevState = previousFiber . memoizedState ;
1096+ }
1097+ const nextState : SuspenseState | null = offscreenFiber . memoizedState ;
1098+
1099+ const wasHidden = prevState !== null ;
1100+ const isHidden = nextState !== null ;
1101+
1102+ const rootState : RootState = finishedRoot . current . memoizedState ;
1103+ // TODO(luna) move pendingSuspenseBoundaries and transitions from
1104+ // HostRoot fiber to FiberRoot
1105+ const rootPendingBoundaries = rootState . pendingSuspenseBoundaries ;
1106+ const rootTransitions = rootState . transitions ;
1107+
1108+ // If there is a name on the suspense boundary, store that in
1109+ // the pending boundaries.
1110+ let name = null ;
1111+ const parent = offscreenFiber . return ;
1112+ if (
1113+ parent !== null &&
1114+ parent . tag === SuspenseComponent &&
1115+ parent . memoizedProps . unstable_name
1116+ ) {
1117+ name = parent . memoizedProps . unstable_name ;
1118+ }
1119+
1120+ if ( rootPendingBoundaries !== null ) {
1121+ if ( previousFiber === null ) {
1122+ // Initial mount
1123+ if ( isHidden ) {
1124+ rootPendingBoundaries . set ( offscreenInstance , {
1125+ name,
1126+ } ) ;
1127+ }
1128+ } else {
1129+ if ( wasHidden && ! isHidden ) {
1130+ // The suspense boundary went from hidden to visible. Remove
1131+ // the boundary from the pending suspense boundaries set
1132+ // if it's there
1133+ if ( rootPendingBoundaries . has ( offscreenInstance ) ) {
1134+ rootPendingBoundaries . delete ( offscreenInstance ) ;
1135+
1136+ if ( rootPendingBoundaries . size === 0 && rootTransitions !== null ) {
1137+ rootTransitions . forEach ( transition => {
1138+ addTransitionCompleteCallbackToPendingTransition ( {
1139+ transitionName : transition . name ,
1140+ startTime : transition . startTime ,
1141+ } ) ;
1142+ } ) ;
1143+ }
1144+ }
1145+ } else if ( ! wasHidden && isHidden ) {
1146+ // The suspense boundaries was just hidden. Add the boundary
1147+ // to the pending boundary set if it's there
1148+ rootPendingBoundaries . set ( offscreenInstance , {
1149+ name,
1150+ } ) ;
1151+ }
1152+ }
1153+ }
1154+ }
1155+ }
1156+
10691157function hideOrUnhideAllChildren ( finishedWork , isHidden ) {
10701158 // Only hide or unhide the top-most host nodes.
10711159 let hostSubtreeRoot = null ;
@@ -2747,22 +2835,48 @@ function commitPassiveMountOnFiber(
27472835 }
27482836
27492837 if ( enableTransitionTracing ) {
2838+ // Get the transitions that were initiatized during the render
2839+ // and add a start transition callback for each of them
2840+ const state = finishedWork . memoizedState ;
2841+ // TODO Since it's a mutable field, this should live on the FiberRoot
2842+ if ( state . transitions === null ) {
2843+ state . transitions = new Set ( [ ] ) ;
2844+ }
2845+ const pendingTransitions = state . transitions ;
2846+ const pendingSuspenseBoundaries = state . pendingSuspenseBoundaries ;
2847+
2848+ // Initial render
27502849 if ( committedTransitions !== null ) {
27512850 committedTransitions . forEach ( transition => {
2752- // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
27532851 addTransitionStartCallbackToPendingTransition ( {
27542852 transitionName : transition . name ,
27552853 startTime : transition . startTime ,
27562854 } ) ;
2855+ pendingTransitions . add ( transition ) ;
2856+ } ) ;
27572857
2758- addTransitionCompleteCallbackToPendingTransition ( {
2759- transitionName : transition . name ,
2760- startTime : transition . startTime ,
2858+ if (
2859+ pendingSuspenseBoundaries === null ||
2860+ pendingSuspenseBoundaries . size === 0
2861+ ) {
2862+ pendingTransitions . forEach ( transition => {
2863+ addTransitionCompleteCallbackToPendingTransition ( {
2864+ transitionName : transition . name ,
2865+ startTime : transition . startTime ,
2866+ } ) ;
27612867 } ) ;
2762- } ) ;
2868+ }
27632869
27642870 clearTransitionsForLanes ( finishedRoot , committedLanes ) ;
2765- finishedWork . memoizedState . transitions = null ;
2871+ }
2872+
2873+ // If there are no more pending suspense boundaries we
2874+ // clear the transitions because they are all complete.
2875+ if (
2876+ pendingSuspenseBoundaries === null ||
2877+ pendingSuspenseBoundaries . size === 0
2878+ ) {
2879+ state . transitions = null ;
27662880 }
27672881 }
27682882 break ;
@@ -2800,9 +2914,44 @@ function commitPassiveMountOnFiber(
28002914 }
28012915
28022916 if ( enableTransitionTracing ) {
2803- // TODO: Add code to actually process the update queue
2917+ const isFallback = finishedWork . memoizedState ;
2918+ const queue = ( finishedWork . updateQueue : any ) ;
2919+ const rootMemoizedState = finishedRoot . current . memoizedState ;
2920+
2921+ if ( queue !== null ) {
2922+ // We have one instance of the pendingSuspenseBoundaries map.
2923+ // We only need one because we update it during the commit phase.
2924+ // We instantiate a new Map if we haven't already
2925+ if ( rootMemoizedState . pendingSuspenseBoundaries === null ) {
2926+ rootMemoizedState . pendingSuspenseBoundaries = new Map ( ) ;
2927+ }
2928+
2929+ if ( isFallback ) {
2930+ const transitions = queue . transitions ;
2931+ let prevTransitions = finishedWork . memoizedState . transitions ;
2932+ // Add all the transitions saved in the update queue during
2933+ // the render phase (ie the transitions associated with this boundary)
2934+ // into the transitions set.
2935+ if ( transitions !== null ) {
2936+ if ( prevTransitions === null ) {
2937+ // We only have one instance of the transitions set
2938+ // because we update it only during the commit phase. We
2939+ // will create the set on a as needed basis in the commit phase
2940+ finishedWork . memoizedState . transitions = prevTransitions = new Set ( ) ;
2941+ }
2942+
2943+ transitions . forEach ( transition => {
2944+ prevTransitions . add ( transition ) ;
2945+ } ) ;
2946+ }
2947+ }
2948+ }
2949+
2950+ commitTransitionProgress ( finishedRoot , finishedWork ) ;
2951+
28042952 finishedWork . updateQueue = null ;
28052953 }
2954+
28062955 break ;
28072956 }
28082957 case CacheComponent : {
0 commit comments