15
import type {Lane} from './ReactFiberLane';
16
import type {Transition} from 'react/src/ReactStartTransition';
17
18
-import {requestTransitionLane} from './ReactFiberRootScheduler';
18
+import {
19
+ requestTransitionLane,
20
+ ensureScheduleIsScheduled,
21
+} from './ReactFiberRootScheduler';
22
import {NoLane} from './ReactFiberLane';
23
import {
24
hasScheduledTransitionWork,
27
import {
28
enableComponentPerformanceTrack,
29
enableProfilerTimer,
30
+ enableDefaultTransitionIndicator,
31
} from 'shared/ReactFeatureFlags';
32
import {clearEntangledAsyncTransitionTypes} from './ReactFiberTransitionTypes';
33
34
+import noop from 'shared/noop';
35
+import reportGlobalError from 'shared/reportGlobalError';
36
+
37
// If there are multiple, concurrent async actions, they are entangled. All
38
// transition updates that occur while the async action is still in progress
39
// are treated as part of the action.
53
// until the async action scope has completed.
54
let currentEntangledActionThenable: Thenable<void> | null = null;
55
56
+// Track the default indicator for every root. undefined means we haven't
57
+// had any roots registered yet. null means there's more than one callback.
58
+// If there's more than one callback we bailout to not supporting isomorphic
59
+// default indicators.
60
+let isomorphicDefaultTransitionIndicator:
61
+ | void
62
+ | null
63
+ | (() => void | (() => void)) = undefined;
64
+// The clean up function for the currently running indicator.
65
+let pendingIsomorphicIndicator: null | (() => void) = null;
66
+// The number of roots that have pending Transitions that depend on the
67
+// started isomorphic indicator.
68
+let pendingEntangledRoots: number = 0;
69
+let needsIsomorphicIndicator: boolean = false;
70
+
71
export function entangleAsyncAction<S>(
72
transition: Transition,
73
thenable: Thenable<S>,
88
},
89
};
90
currentEntangledActionThenable = entangledThenable;
91
+ if (enableDefaultTransitionIndicator) {
92
+ needsIsomorphicIndicator = true;
93
+ // We'll check if we need a default indicator in a microtask. Ensure
94
+ // we have this scheduled even if no root is scheduled.
95
+ ensureScheduleIsScheduled();
96
+ }
97
}
98
currentEntangledPendingCount++;
99
thenable.then(pingEngtangledActionScope, pingEngtangledActionScope);
114
}
115
}
116
clearEntangledAsyncTransitionTypes();
117
+ if (pendingEntangledRoots === 0) {
118
+ stopIsomorphicDefaultIndicator();
119
+ }
120
if (currentEntangledListeners !== null) {
121
// All the actions have finished. Close the entangled async action scope
122
// and notify all the listeners.
129
currentEntangledListeners = null;
130
currentEntangledLane = NoLane;
131
currentEntangledActionThenable = null;
132
+ needsIsomorphicIndicator = false;
133
for (let i = 0; i < listeners.length; i++) {
134
const listener = listeners[i];
135
listener();
193
export function peekEntangledActionThenable(): Thenable<void> | null {
194
return currentEntangledActionThenable;
195
}
196
+
197
+export function registerDefaultIndicator(
198
+ onDefaultTransitionIndicator: () => void | (() => void),
199
+): void {
200
+ if (!enableDefaultTransitionIndicator) {
201
+ return;
202
+ }
203
+ if (isomorphicDefaultTransitionIndicator === undefined) {
204
+ isomorphicDefaultTransitionIndicator = onDefaultTransitionIndicator;
205
+ } else if (
206
+ isomorphicDefaultTransitionIndicator !== onDefaultTransitionIndicator
207
+ ) {
208
+ isomorphicDefaultTransitionIndicator = null;
209
+ // Stop any on-going indicator since it's now ambiguous.
210
+ stopIsomorphicDefaultIndicator();
211
+ }
212
+}
213
+
214
+export function startIsomorphicDefaultIndicatorIfNeeded() {
215
+ if (!enableDefaultTransitionIndicator) {
216
+ return;
217
+ }
218
+ if (!needsIsomorphicIndicator) {
219
+ return;
220
+ }
221
+ if (
222
+ isomorphicDefaultTransitionIndicator != null &&
223
+ pendingIsomorphicIndicator === null
224
+ ) {
225
+ try {
226
+ pendingIsomorphicIndicator =
227
+ isomorphicDefaultTransitionIndicator() || noop;
228
+ } catch (x) {
229
+ pendingIsomorphicIndicator = noop;
230
+ reportGlobalError(x);
231
+ }
232
+ }
233
+}
234
+
235
+function stopIsomorphicDefaultIndicator() {
236
+ if (!enableDefaultTransitionIndicator) {
237
+ return;
238
+ }
239
+ if (pendingIsomorphicIndicator !== null) {
240
+ const cleanup = pendingIsomorphicIndicator;
241
+ pendingIsomorphicIndicator = null;
242
+ cleanup();
243
+ }
244
+}
245
+
246
+function releaseIsomorphicIndicator() {
247
+ if (--pendingEntangledRoots === 0) {
248
+ stopIsomorphicDefaultIndicator();
249
+ }
250
+}
251
+
252
+export function hasOngoingIsomorphicIndicator(): boolean {
253
+ return pendingIsomorphicIndicator !== null;
254
+}
255
+
256
+export function retainIsomorphicIndicator(): () => void {
257
+ pendingEntangledRoots++;
258
+ return releaseIsomorphicIndicator;
259
+}
260
+
261
+export function markIsomorphicIndicatorHandled(): void {
262
+ needsIsomorphicIndicator = false;
263
+}