@@ -63,6 +63,7 @@ import {
63
63
OffscreenComponent ,
64
64
LegacyHiddenComponent ,
65
65
CacheComponent ,
66
+ TracingMarkerComponent ,
66
67
} from './ReactWorkTags' ;
67
68
import { detachDeletedInstance } from './ReactFiberHostConfig' ;
68
69
import {
@@ -1002,7 +1003,8 @@ function commitLayoutEffectOnFiber(
1002
1003
case IncompleteClassComponent:
1003
1004
case ScopeComponent:
1004
1005
case OffscreenComponent:
1005
- case LegacyHiddenComponent: {
1006
+ case LegacyHiddenComponent:
1007
+ case TracingMarkerComponent: {
1006
1008
break ;
1007
1009
}
1008
1010
@@ -1067,6 +1069,77 @@ function reappearLayoutEffectsOnFiber(node: Fiber) {
1067
1069
}
1068
1070
}
1069
1071
1072
+ function commitTransitionProgress (
1073
+ finishedRoot : FiberRoot ,
1074
+ offscreenFiber : Fiber ,
1075
+ ) {
1076
+ // This function adds suspense boundaries to the root
1077
+ // or tracing marker's pendingSuspenseBoundaries map.
1078
+ // When a suspense boundary goes from a resolved to a fallback
1079
+ // state we add the boundary to the map, and when it goes from
1080
+ // a fallback to a resolved state, we remove the boundary from
1081
+ // the map.
1082
+
1083
+ // We use stateNode on the Offscreen component as a stable object
1084
+ // that doesnt change from render to render. This way we can
1085
+ // distinguish between different Offscreen instances (vs. the same
1086
+ // Offscreen instance with different fibers)
1087
+ const offscreenInstance : OffscreenInstance = offscreenFiber . stateNode ;
1088
+
1089
+ let prevState : SuspenseState | null = null ;
1090
+ const previousFiber = offscreenFiber . alternate ;
1091
+ if ( previousFiber !== null && previousFiber . memoizedState !== null ) {
1092
+ prevState = previousFiber . memoizedState ;
1093
+ }
1094
+ const nextState : SuspenseState | null = offscreenFiber . memoizedState ;
1095
+
1096
+ const wasHidden = prevState !== null ;
1097
+ const isHidden = nextState !== null ;
1098
+
1099
+ const rootState : RootState = finishedRoot . current . memoizedState ;
1100
+ // TODO(luna) move pendingSuspenseBoundaries and transitions from
1101
+ // HostRoot fiber to FiberRoot
1102
+ const rootPendingBoundaries = rootState . pendingSuspenseBoundaries ;
1103
+
1104
+ // If there is a name on the suspense boundary, store that in
1105
+ // the pending boundaries.
1106
+ let name = null ;
1107
+ const parent = offscreenFiber . return ;
1108
+ if (
1109
+ parent !== null &&
1110
+ parent . tag === SuspenseComponent &&
1111
+ parent . memoizedProps . unstable_name
1112
+ ) {
1113
+ name = parent . memoizedProps . unstable_name ;
1114
+ }
1115
+
1116
+ if ( rootPendingBoundaries !== null ) {
1117
+ if ( previousFiber === null ) {
1118
+ // Initial mount
1119
+ if ( isHidden ) {
1120
+ rootPendingBoundaries . set ( offscreenInstance , {
1121
+ name,
1122
+ } ) ;
1123
+ }
1124
+ } else {
1125
+ if ( wasHidden && ! isHidden ) {
1126
+ // The suspense boundary went from hidden to visible. Remove
1127
+ // the boundary from the pending suspense boundaries set
1128
+ // if it's there
1129
+ if ( rootPendingBoundaries . has ( offscreenInstance ) ) {
1130
+ rootPendingBoundaries . delete ( offscreenInstance ) ;
1131
+ }
1132
+ } else if ( ! wasHidden && isHidden ) {
1133
+ // The suspense boundaries was just hidden. Add the boundary
1134
+ // to the pending boundary set if it's there
1135
+ rootPendingBoundaries . set ( offscreenInstance , {
1136
+ name,
1137
+ } ) ;
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+
1070
1143
function hideOrUnhideAllChildren ( finishedWork , isHidden ) {
1071
1144
// Only hide or unhide the top-most host nodes.
1072
1145
let hostSubtreeRoot = null ;
@@ -2730,22 +2803,49 @@ function commitPassiveMountOnFiber(
2730
2803
}
2731
2804
2732
2805
if ( enableTransitionTracing ) {
2733
- if ( committedTransitions !== null ) {
2806
+ // Get the transitions that were initiatized during the render
2807
+ // and add a start transition callback for each of them
2808
+ const state = finishedWork . memoizedState ;
2809
+ if ( state . transitions === null ) {
2810
+ state . transitions = new Set ( [ ] ) ;
2811
+ }
2812
+ const pendingTransitions = state . transitions ;
2813
+
2814
+ if ( committedTransitions != null ) {
2734
2815
committedTransitions . forEach ( transition => {
2735
- // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2736
2816
addTransitionStartCallbackToPendingTransition ( {
2737
2817
transitionName : transition . name ,
2738
2818
startTime : transition . startTime ,
2739
2819
} ) ;
2820
+ pendingTransitions . add ( transition ) ;
2821
+ } ) ;
2822
+
2823
+ clearTransitionsForLanes ( finishedRoot , committedLanes ) ;
2824
+ }
2740
2825
2826
+ const pendingSuspenseBoundaries = state . pendingSuspenseBoundaries ;
2827
+ // process the lazy transitions list by filtering duplicate transitions
2828
+ // and calling the transition complete callback on all transitions
2829
+ // if there are no more pending suspense boundaries
2830
+ pendingTransitions . forEach ( transition => {
2831
+ if (
2832
+ pendingSuspenseBoundaries === null ||
2833
+ pendingSuspenseBoundaries . size === 0
2834
+ ) {
2741
2835
addTransitionCompleteCallbackToPendingTransition ( {
2742
2836
transitionName : transition . name ,
2743
2837
startTime : transition . startTime ,
2744
2838
} ) ;
2745
- } ) ;
2839
+ }
2840
+ } ) ;
2746
2841
2747
- clearTransitionsForLanes ( finishedRoot , committedLanes ) ;
2748
- finishedWork . memoizedState . transitions = null ;
2842
+ // If there are no more pending suspense boundaries we
2843
+ // clear the transitions because they are all complete.
2844
+ if (
2845
+ pendingSuspenseBoundaries === null ||
2846
+ pendingSuspenseBoundaries . size === 0
2847
+ ) {
2848
+ state . transitions = null ;
2749
2849
}
2750
2850
}
2751
2851
break ;
@@ -2783,9 +2883,44 @@ function commitPassiveMountOnFiber(
2783
2883
}
2784
2884
2785
2885
if ( enableTransitionTracing ) {
2786
- // TODO: Add code to actually process the update queue
2886
+ const isFallback = finishedWork . memoizedState ;
2887
+ const queue = ( finishedWork . updateQueue : any ) ;
2888
+ const rootMemoizedState = finishedRoot . current . memoizedState ;
2889
+
2890
+ if ( queue !== null ) {
2891
+ // We have one instance of the pendingSuspenseBoundaries map.
2892
+ // We only need one because we update it during the commit phase.
2893
+ // We instantiate a new Map if we haven't already
2894
+ if ( rootMemoizedState . pendingSuspenseBoundaries === null ) {
2895
+ rootMemoizedState . pendingSuspenseBoundaries = new Map ( ) ;
2896
+ }
2897
+
2898
+ if ( isFallback ) {
2899
+ const transitions = queue . transitions ;
2900
+ let prevTransitions = finishedWork . memoizedState . transitions ;
2901
+ // Add all the transitions saved in the update queue during
2902
+ // the render phase (ie the transitions associated with this boundary)
2903
+ // into the transitions set.
2904
+ if ( transitions != null ) {
2905
+ if ( prevTransitions === null ) {
2906
+ // We only have one instance of the transitions set
2907
+ // because we update it only during the commit phase. We
2908
+ // will create the set on a as needed basis in the commit phase
2909
+ finishedWork . memoizedState . transitions = prevTransitions = new Set ( ) ;
2910
+ }
2911
+
2912
+ transitions . forEach ( transition => {
2913
+ prevTransitions . add ( transition ) ;
2914
+ } ) ;
2915
+ }
2916
+ }
2917
+ }
2918
+
2919
+ commitTransitionProgress ( finishedRoot , finishedWork ) ;
2920
+
2787
2921
finishedWork . updateQueue = null ;
2788
2922
}
2923
+
2789
2924
break ;
2790
2925
}
2791
2926
case CacheComponent : {
0 commit comments