@@ -22,11 +22,17 @@ 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' ;
29
- import type { Transition } from './ReactFiberTracingMarkerComponent.new' ;
32
+ import type {
33
+ Transition ,
34
+ PendingSuspenseBoundaries ,
35
+ } from './ReactFiberTracingMarkerComponent.new' ;
30
36
31
37
import {
32
38
enableCreateEventHandleAPI ,
@@ -63,6 +69,7 @@ import {
63
69
OffscreenComponent ,
64
70
LegacyHiddenComponent ,
65
71
CacheComponent ,
72
+ TracingMarkerComponent ,
66
73
} from './ReactWorkTags' ;
67
74
import { detachDeletedInstance } from './ReactFiberHostConfig' ;
68
75
import {
@@ -1002,7 +1009,8 @@ function commitLayoutEffectOnFiber(
1002
1009
case IncompleteClassComponent:
1003
1010
case ScopeComponent:
1004
1011
case OffscreenComponent:
1005
- case LegacyHiddenComponent: {
1012
+ case LegacyHiddenComponent:
1013
+ case TracingMarkerComponent: {
1006
1014
break ;
1007
1015
}
1008
1016
@@ -1067,6 +1075,77 @@ function reappearLayoutEffectsOnFiber(node: Fiber) {
1067
1075
}
1068
1076
}
1069
1077
1078
+ function commitTransitionProgress (
1079
+ finishedRoot : FiberRoot ,
1080
+ offscreenFiber : 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 : OffscreenInstance = offscreenFiber . stateNode ;
1094
+
1095
+ let prevState : SuspenseState | null = null ;
1096
+ const previousFiber = offscreenFiber . alternate ;
1097
+ if ( previousFiber !== null && previousFiber . memoizedState !== null ) {
1098
+ prevState = previousFiber . memoizedState ;
1099
+ }
1100
+ const nextState : SuspenseState | null = offscreenFiber . memoizedState ;
1101
+
1102
+ const wasHidden = prevState !== null ;
1103
+ const isHidden = nextState !== null ;
1104
+
1105
+ const rootState : RootState = finishedRoot . current . memoizedState ;
1106
+ // TODO(luna) move pendingSuspenseBoundaries and transitions from
1107
+ // HostRoot fiber to FiberRoot
1108
+ const rootPendingBoundaries = rootState . 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 = offscreenFiber . 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 ( previousFiber === 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
+
1070
1149
function hideOrUnhideAllChildren ( finishedWork , isHidden ) {
1071
1150
// Only hide or unhide the top-most host nodes.
1072
1151
let hostSubtreeRoot = null ;
@@ -2730,22 +2809,53 @@ function commitPassiveMountOnFiber(
2730
2809
}
2731
2810
2732
2811
if ( enableTransitionTracing ) {
2733
- if ( committedTransitions !== null ) {
2812
+ // Get the transitions that were initiatized during the render
2813
+ // and add a start transition callback for each of them
2814
+ const state = finishedWork . memoizedState ;
2815
+ if ( state . transitions === null ) {
2816
+ state . transitions = new Set ( [ ] ) ;
2817
+ }
2818
+ const pendingTransitions = state . transitions ;
2819
+
2820
+ if ( committedTransitions != null ) {
2734
2821
committedTransitions . forEach ( transition => {
2735
- // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2736
2822
addTransitionStartCallbackToPendingTransition ( {
2737
2823
transitionName : transition . name ,
2738
2824
startTime : transition . startTime ,
2739
2825
} ) ;
2740
-
2741
- addTransitionCompleteCallbackToPendingTransition ( {
2742
- transitionName : transition . name ,
2743
- startTime : transition . startTime ,
2744
- } ) ;
2826
+ pendingTransitions . add ( transition ) ;
2745
2827
} ) ;
2746
2828
2747
2829
clearTransitionsForLanes ( finishedRoot , committedLanes ) ;
2748
- finishedWork . memoizedState . transitions = null ;
2830
+ }
2831
+
2832
+ const pendingSuspenseBoundaries = state . pendingSuspenseBoundaries ;
2833
+ const processedTransitions = new Set ( ) ;
2834
+ // process the lazy transitions list by filtering duplicate transitions
2835
+ // and calling the transition complete callback on all transitions
2836
+ // if there are no more pending suspense boundaries
2837
+ pendingTransitions . forEach ( transition => {
2838
+ if ( ! processedTransitions . has ( transition ) ) {
2839
+ if (
2840
+ pendingSuspenseBoundaries === null ||
2841
+ pendingSuspenseBoundaries . size === 0
2842
+ ) {
2843
+ addTransitionCompleteCallbackToPendingTransition ( {
2844
+ transitionName : transition . name ,
2845
+ startTime : transition . startTime ,
2846
+ } ) ;
2847
+ }
2848
+ processedTransitions . add ( transition ) ;
2849
+ }
2850
+ } ) ;
2851
+
2852
+ // If there are no more pending suspense boundaries we
2853
+ // clear the transitions because they are all complete.
2854
+ if (
2855
+ pendingSuspenseBoundaries === null ||
2856
+ pendingSuspenseBoundaries . size === 0
2857
+ ) {
2858
+ state . transitions = null ;
2749
2859
}
2750
2860
}
2751
2861
break ;
@@ -2783,9 +2893,44 @@ function commitPassiveMountOnFiber(
2783
2893
}
2784
2894
2785
2895
if ( enableTransitionTracing ) {
2786
- // TODO: Add code to actually process the update queue
2896
+ const isFallback = finishedWork . memoizedState ;
2897
+ const queue = ( finishedWork . updateQueue : any ) ;
2898
+ const rootMemoizedState = finishedRoot . current . memoizedState ;
2899
+
2900
+ if ( queue !== null ) {
2901
+ // We have one instance of the pendingSuspenseBoundaries map.
2902
+ // We only need one because we update it during the commit phase.
2903
+ // We instantiate a new Map if we haven't already
2904
+ if ( rootMemoizedState . pendingSuspenseBoundaries === null ) {
2905
+ rootMemoizedState . pendingSuspenseBoundaries = new Map ( ) ;
2906
+ }
2907
+
2908
+ if ( isFallback ) {
2909
+ const transitions = queue . transitions ;
2910
+ let prevTransitions = finishedWork . memoizedState . transitions ;
2911
+ // Add all the transitions saved in the update queue during
2912
+ // the render phase (ie the transitions associated with this boundary)
2913
+ // into the transitions set.
2914
+ if ( transitions != null ) {
2915
+ if ( prevTransitions === null ) {
2916
+ // We only have one instance of the transitions set
2917
+ // because we update it only during the commit phase. We
2918
+ // will create the set on a as needed basis in the commit phase
2919
+ finishedWork . memoizedState . transitions = prevTransitions = new Set ( ) ;
2920
+ }
2921
+
2922
+ transitions . forEach ( transition => {
2923
+ prevTransitions . add ( transition ) ;
2924
+ } ) ;
2925
+ }
2926
+ }
2927
+ }
2928
+
2929
+ commitTransitionProgress ( finishedRoot , finishedWork ) ;
2930
+
2787
2931
finishedWork . updateQueue = null ;
2788
2932
}
2933
+
2789
2934
break ;
2790
2935
}
2791
2936
case CacheComponent : {
0 commit comments