@@ -22,11 +22,17 @@ 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' ;
29- import type { Transition } from './ReactFiberTracingMarkerComponent.new' ;
32+ import type {
33+ Transition ,
34+ PendingSuspenseBoundaries ,
35+ } from './ReactFiberTracingMarkerComponent.new' ;
3036
3137import {
3238 enableCreateEventHandleAPI ,
@@ -63,6 +69,7 @@ import {
6369 OffscreenComponent ,
6470 LegacyHiddenComponent ,
6571 CacheComponent ,
72+ TracingMarkerComponent ,
6673} from './ReactWorkTags' ;
6774import { detachDeletedInstance } from './ReactFiberHostConfig' ;
6875import {
@@ -1002,7 +1009,8 @@ function commitLayoutEffectOnFiber(
10021009 case IncompleteClassComponent:
10031010 case ScopeComponent:
10041011 case OffscreenComponent:
1005- case LegacyHiddenComponent: {
1012+ case LegacyHiddenComponent:
1013+ case TracingMarkerComponent: {
10061014 break ;
10071015 }
10081016
@@ -1067,6 +1075,77 @@ function reappearLayoutEffectsOnFiber(node: Fiber) {
10671075 }
10681076}
10691077
1078+ function addOrRemovePendingBoundariesOnRoot (
1079+ finishedRoot : FiberRoot ,
1080+ finishedWork : Fiber ,
1081+ ) {
1082+ // This function adds suspense boundaries to the root
1083+ // or tracing marker's pendingSuspenseBoundaries map.
1084+ // When a suspense boundary goes from a resolved to a fallback
1085+ // state we add the boundary to the map, and when it goes from
1086+ // a fallback to a resolved state, we remove the boundary from
1087+ // the map.
1088+
1089+ // We use stateNode on the Offscreen component as a stable object
1090+ // that doesnt change from render to render. This way we can
1091+ // distinguish between different Offscreen instances (vs. the same
1092+ // Offscreen instance with different fibers)
1093+ const offscreenInstance = finishedWork . stateNode ;
1094+
1095+ let prevState : SuspenseState | null = null ;
1096+ if (
1097+ finishedWork . alternate !== null &&
1098+ finishedWork . alternate . memoizedState !== null
1099+ ) {
1100+ prevState = finishedWork . alternate . memoizedState ;
1101+ }
1102+ const nextState : SuspenseState | null = finishedWork . memoizedState ;
1103+
1104+ const wasHidden = prevState !== null ;
1105+ const isHidden = nextState !== null ;
1106+
1107+ const rootPendingBoundaries =
1108+ finishedRoot . current . memoizedState . pendingSuspenseBoundaries ;
1109+
1110+ // If there is a name on the suspense boundary, store that in
1111+ // the pending boundaries.
1112+ let name = null ;
1113+ const parent = finishedWork . return ;
1114+ if (
1115+ parent !== null &&
1116+ parent . tag === SuspenseComponent &&
1117+ parent . memoizedProps . unstable_name
1118+ ) {
1119+ name = parent . memoizedProps . unstable_name ;
1120+ }
1121+
1122+ if ( rootPendingBoundaries !== null ) {
1123+ if ( finishedWork . alternate === null ) {
1124+ // Initial mount
1125+ if ( isHidden ) {
1126+ rootPendingBoundaries . set ( offscreenInstance , {
1127+ name,
1128+ } ) ;
1129+ }
1130+ } else {
1131+ if ( wasHidden && ! isHidden ) {
1132+ // The suspense boundary went from hidden to visible. Remove
1133+ // the boundary from the pending suspense boundaries set
1134+ // if it's there
1135+ if ( rootPendingBoundaries . has ( offscreenInstance ) ) {
1136+ rootPendingBoundaries . delete ( offscreenInstance ) ;
1137+ }
1138+ } else if ( ! wasHidden && isHidden ) {
1139+ // The suspense boundaries was just hidden. Add the boundary
1140+ // to the pending boundary set if it's there
1141+ rootPendingBoundaries . set ( offscreenInstance , {
1142+ name,
1143+ } ) ;
1144+ }
1145+ }
1146+ }
1147+ }
1148+
10701149function hideOrUnhideAllChildren ( finishedWork , isHidden ) {
10711150 // Only hide or unhide the top-most host nodes.
10721151 let hostSubtreeRoot = null ;
@@ -2725,22 +2804,54 @@ function commitPassiveMountOnFiber(
27252804 }
27262805
27272806 if ( enableTransitionTracing ) {
2728- if ( committedTransitions !== null ) {
2807+ // Get the transitions that were initiatized during the render
2808+ // and add a start transition callback for each of them
2809+ const state = finishedWork . memoizedState ;
2810+ if ( state . transitions === null ) {
2811+ state . transitions = new Set ( [ ] ) ;
2812+ }
2813+ const pendingTransitions = state . transitions ;
2814+
2815+ if ( committedTransitions != null ) {
27292816 committedTransitions . forEach ( transition => {
2730- // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
27312817 addTransitionStartCallbackToPendingTransition ( {
27322818 transitionName : transition . name ,
27332819 startTime : transition . startTime ,
27342820 } ) ;
2735-
2736- addTransitionCompleteCallbackToPendingTransition ( {
2737- transitionName : transition . name ,
2738- startTime : transition . startTime ,
2739- } ) ;
2821+ pendingTransitions . add ( transition ) ;
27402822 } ) ;
27412823
27422824 clearTransitionsForLanes ( finishedRoot , committedLanes ) ;
2743- finishedWork . memoizedState . transitions = null ;
2825+ }
2826+
2827+ const pendingSuspenseBoundaries = state . pendingSuspenseBoundaries ;
2828+ const processedTransitions = new Set ( ) ;
2829+ // process the lazy transitions list by filtering duplicate transitions
2830+ // and calling the transition complete callback on all transitions
2831+ // if there are no more pending suspense boundaries
2832+ pendingTransitions . forEach ( transition => {
2833+ if ( ! processedTransitions . has ( transition ) ) {
2834+ if (
2835+ pendingSuspenseBoundaries === null ||
2836+ pendingSuspenseBoundaries . size === 0
2837+ ) {
2838+ addTransitionCompleteCallbackToPendingTransition ( {
2839+ transitionName : transition . name ,
2840+ startTime : transition . startTime ,
2841+ } ) ;
2842+ }
2843+ processedTransitions . add ( transition ) ;
2844+ }
2845+ } ) ;
2846+
2847+ // If there are no more pending suspense boundaries we
2848+ // clear the transitions because they are all complete. Otherwise
2849+ // we store the transitions where we remove all duplicates
2850+ if (
2851+ pendingSuspenseBoundaries === null ||
2852+ pendingSuspenseBoundaries . size === 0
2853+ ) {
2854+ state . transitions = null ;
27442855 }
27452856 }
27462857 break ;
@@ -2776,6 +2887,46 @@ function commitPassiveMountOnFiber(
27762887 }
27772888 }
27782889 }
2890+
2891+ if ( enableTransitionTracing ) {
2892+ const isFallback = finishedWork . memoizedState ;
2893+ const queue = ( finishedWork . updateQueue : any ) ;
2894+ const rootMemoizedState = finishedRoot . current . memoizedState ;
2895+
2896+ if ( queue !== null ) {
2897+ // We have one instance of the pendingSuspenseBoundaries map.
2898+ // We only need one because we update it during the commit phase.
2899+ // We instantiate a new Map if we haven't already
2900+ if ( rootMemoizedState . pendingSuspenseBoundaries === null ) {
2901+ rootMemoizedState . pendingSuspenseBoundaries = new Map ( ) ;
2902+ }
2903+
2904+ if ( isFallback ) {
2905+ const transitions = queue . transitions ;
2906+ let prevTransitions = finishedWork . memoizedState . transitions ;
2907+ // Add all the transitions saved in the update queue during
2908+ // the render phase (ie the transitions associated with this boundary)
2909+ // into the transitions set.
2910+ if ( transitions != null ) {
2911+ if ( prevTransitions === null ) {
2912+ // We only have one instance of the transitions set
2913+ // because we update it only during the commit phase. We
2914+ // will create the set on a as needed basis in the commit phase
2915+ finishedWork . memoizedState . transitions = prevTransitions = new Set ( ) ;
2916+ }
2917+
2918+ transitions . forEach ( transition => {
2919+ prevTransitions . add ( transition ) ;
2920+ } ) ;
2921+ }
2922+ }
2923+ }
2924+
2925+ addOrRemovePendingBoundariesOnRoot ( finishedRoot , finishedWork ) ;
2926+
2927+ finishedWork . updateQueue = null ;
2928+ }
2929+
27792930 break ;
27802931 }
27812932 case CacheComponent : {
0 commit comments