@@ -15,7 +15,10 @@ import type {
15
15
import type { Lane } from './ReactFiberLane' ;
16
16
import type { Transition } from 'react/src/ReactStartTransition' ;
17
17
18
- import { requestTransitionLane } from './ReactFiberRootScheduler' ;
18
+ import {
19
+ requestTransitionLane ,
20
+ ensureScheduleIsScheduled ,
21
+ } from './ReactFiberRootScheduler' ;
19
22
import { NoLane } from './ReactFiberLane' ;
20
23
import {
21
24
hasScheduledTransitionWork ,
@@ -24,9 +27,13 @@ import {
24
27
import {
25
28
enableComponentPerformanceTrack ,
26
29
enableProfilerTimer ,
30
+ enableDefaultTransitionIndicator ,
27
31
} from 'shared/ReactFeatureFlags' ;
28
32
import { clearEntangledAsyncTransitionTypes } from './ReactFiberTransitionTypes' ;
29
33
34
+ import noop from 'shared/noop' ;
35
+ import reportGlobalError from 'shared/reportGlobalError' ;
36
+
30
37
// If there are multiple, concurrent async actions, they are entangled. All
31
38
// transition updates that occur while the async action is still in progress
32
39
// are treated as part of the action.
@@ -46,6 +53,21 @@ let currentEntangledLane: Lane = NoLane;
46
53
// until the async action scope has completed.
47
54
let currentEntangledActionThenable : Thenable < void > | null = null ;
48
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
+
49
71
export function entangleAsyncAction < S > (
50
72
transition : Transition ,
51
73
thenable : Thenable < S > ,
@@ -66,6 +88,12 @@ export function entangleAsyncAction<S>(
66
88
} ,
67
89
} ;
68
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
+ }
69
97
}
70
98
currentEntangledPendingCount ++ ;
71
99
thenable . then ( pingEngtangledActionScope , pingEngtangledActionScope ) ;
@@ -86,6 +114,9 @@ function pingEngtangledActionScope() {
86
114
}
87
115
}
88
116
clearEntangledAsyncTransitionTypes ( ) ;
117
+ if ( pendingEntangledRoots === 0 ) {
118
+ stopIsomorphicDefaultIndicator ( ) ;
119
+ }
89
120
if ( currentEntangledListeners !== null ) {
90
121
// All the actions have finished. Close the entangled async action scope
91
122
// and notify all the listeners.
@@ -98,6 +129,7 @@ function pingEngtangledActionScope() {
98
129
currentEntangledListeners = null ;
99
130
currentEntangledLane = NoLane ;
100
131
currentEntangledActionThenable = null ;
132
+ needsIsomorphicIndicator = false ;
101
133
for ( let i = 0 ; i < listeners . length ; i ++ ) {
102
134
const listener = listeners [ i ] ;
103
135
listener ( ) ;
@@ -161,3 +193,71 @@ export function peekEntangledActionLane(): Lane {
161
193
export function peekEntangledActionThenable ( ) : Thenable < void > | null {
162
194
return currentEntangledActionThenable ;
163
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
+ }
0 commit comments