@@ -22,7 +22,10 @@ import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
22
22
import type { UpdateQueue } from './ReactUpdateQueue.new' ;
23
23
import type { FunctionComponentUpdateQueue } from './ReactFiberHooks.new' ;
24
24
import type { Wakeable } from 'shared/ReactTypes' ;
25
- import type { OffscreenState } from './ReactFiberOffscreenComponent' ;
25
+ import type {
26
+ OffscreenState ,
27
+ OffscreenInstance ,
28
+ } from './ReactFiberOffscreenComponent' ;
26
29
import type { HookFlags } from './ReactHookEffectTags' ;
27
30
import type { Cache } from './ReactFiberCacheComponent.new' ;
28
31
import type { RootState } from './ReactFiberRoot.new' ;
@@ -62,6 +65,7 @@ import {
62
65
OffscreenComponent ,
63
66
LegacyHiddenComponent ,
64
67
CacheComponent ,
68
+ TracingMarkerComponent ,
65
69
} from './ReactWorkTags' ;
66
70
import { detachDeletedInstance } from './ReactFiberHostConfig' ;
67
71
import {
@@ -1001,7 +1005,8 @@ function commitLayoutEffectOnFiber(
1001
1005
case IncompleteClassComponent:
1002
1006
case ScopeComponent:
1003
1007
case OffscreenComponent:
1004
- case LegacyHiddenComponent: {
1008
+ case LegacyHiddenComponent:
1009
+ case TracingMarkerComponent: {
1005
1010
break ;
1006
1011
}
1007
1012
@@ -1066,6 +1071,89 @@ function reappearLayoutEffectsOnFiber(node: Fiber) {
1066
1071
}
1067
1072
}
1068
1073
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
+
1069
1157
function hideOrUnhideAllChildren ( finishedWork , isHidden ) {
1070
1158
// Only hide or unhide the top-most host nodes.
1071
1159
let hostSubtreeRoot = null ;
@@ -2747,22 +2835,48 @@ function commitPassiveMountOnFiber(
2747
2835
}
2748
2836
2749
2837
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
2750
2849
if ( committedTransitions !== null ) {
2751
2850
committedTransitions . forEach ( transition => {
2752
- // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2753
2851
addTransitionStartCallbackToPendingTransition ( {
2754
2852
transitionName : transition . name ,
2755
2853
startTime : transition . startTime ,
2756
2854
} ) ;
2855
+ pendingTransitions . add ( transition ) ;
2856
+ } ) ;
2757
2857
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
+ } ) ;
2761
2867
} ) ;
2762
- } ) ;
2868
+ }
2763
2869
2764
2870
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 ;
2766
2880
}
2767
2881
}
2768
2882
break ;
@@ -2800,9 +2914,44 @@ function commitPassiveMountOnFiber(
2800
2914
}
2801
2915
2802
2916
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
+
2804
2952
finishedWork . updateQueue = null ;
2805
2953
}
2954
+
2806
2955
break ;
2807
2956
}
2808
2957
case CacheComponent : {
0 commit comments