22
import type {UpdateQueue} from './ReactUpdateQueue.new';
23
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
24
import type {Wakeable} from 'shared/ReactTypes';
25
-import type {OffscreenState} from './ReactFiberOffscreenComponent';
25
+import type {
26
+ OffscreenState,
27
+ OffscreenInstance,
28
+} from './ReactFiberOffscreenComponent';
29
import type {HookFlags} from './ReactHookEffectTags';
30
import type {Cache} from './ReactFiberCacheComponent.new';
31
import type {RootState} from './ReactFiberRoot.new';
65
OffscreenComponent,
66
LegacyHiddenComponent,
67
CacheComponent,
68
+ TracingMarkerComponent,
69
} from './ReactWorkTags';
70
import {detachDeletedInstance} from './ReactFiberHostConfig';
71
import {
1005
case IncompleteClassComponent:
1006
case ScopeComponent:
1007
case OffscreenComponent:
1004
- case LegacyHiddenComponent: {
1008
+ case LegacyHiddenComponent:
1009
+ case TracingMarkerComponent: {
1010
break;
1011
}
1012
1071
}
1072
}
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
+
1157
function hideOrUnhideAllChildren(finishedWork, isHidden) {
1158
// Only hide or unhide the top-most host nodes.
1159
let hostSubtreeRoot = null;
2835
}
2836
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
2849
if (committedTransitions !== null) {
2850
committedTransitions.forEach(transition => {
2752
- // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2851
addTransitionStartCallbackToPendingTransition({
2852
transitionName: transition.name,
2853
startTime: transition.startTime,
2854
});
2855
+ pendingTransitions.add(transition);
2856
+ });
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
+ });
2867
});
2762
- });
2868
+ }
2869
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;
2880
}
2881
}
2882
break;
2914
}
2915
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
+
2952
finishedWork.updateQueue = null;
2953
}
2954
+
2955
break;
2956
}
2957
case CacheComponent: {