@@ -19,6 +19,7 @@ import type {Interaction} from 'scheduler/src/Tracing';
19
19
import {
20
20
warnAboutDeprecatedLifecycles ,
21
21
enableUserTimingAPI ,
22
+ enableUpdaterTracking ,
22
23
enableSuspenseServerRenderer ,
23
24
replayFailedUnitOfWorkWithInvokeGuardedCallback ,
24
25
enableProfilerTimer ,
@@ -337,6 +338,16 @@ export function scheduleUpdateOnFiber(
337
338
return ;
338
339
}
339
340
341
+ if ( enableUpdaterTracking ) {
342
+ const pendingUpdatersMap = root . pendingUpdatersMap ;
343
+ let updaters = pendingUpdatersMap . get ( expirationTime ) ;
344
+ if ( updaters == null ) {
345
+ updaters = new Set ( ) ;
346
+ pendingUpdatersMap . set ( expirationTime , updaters ) ;
347
+ }
348
+ updaters . add ( fiber ) ;
349
+ }
350
+
340
351
root . pingTime = NoWork ;
341
352
342
353
checkForInterruption ( fiber , expirationTime ) ;
@@ -1292,6 +1303,12 @@ function commitRootImpl(root) {
1292
1303
// This usually means we've finished all the work, but it can also happen
1293
1304
// when something gets downprioritized during render, like a hidden tree.
1294
1305
root . lastPendingTime = firstPendingTimeBeforeCommit ;
1306
+
1307
+ if ( enableUpdaterTracking ) {
1308
+ if ( firstPendingTimeBeforeCommit !== NoWork ) {
1309
+ restorePendingUpdaters ( root , root . lastPendingTime ) ;
1310
+ }
1311
+ }
1295
1312
}
1296
1313
1297
1314
if ( root === workInProgressRoot ) {
@@ -1377,7 +1394,13 @@ function commitRootImpl(root) {
1377
1394
nextEffect = firstEffect ;
1378
1395
do {
1379
1396
if ( __DEV__ ) {
1380
- invokeGuardedCallback ( null , commitMutationEffects , null ) ;
1397
+ invokeGuardedCallback (
1398
+ null ,
1399
+ commitMutationEffects ,
1400
+ null ,
1401
+ root ,
1402
+ expirationTime ,
1403
+ ) ;
1381
1404
if ( hasCaughtError ( ) ) {
1382
1405
invariant ( nextEffect !== null , 'Should be working on an effect.' ) ;
1383
1406
const error = clearCaughtError ( ) ;
@@ -1386,7 +1409,7 @@ function commitRootImpl(root) {
1386
1409
}
1387
1410
} else {
1388
1411
try {
1389
- commitMutationEffects ( ) ;
1412
+ commitMutationEffects ( root , expirationTime ) ;
1390
1413
} catch ( error ) {
1391
1414
invariant ( nextEffect !== null , 'Should be working on an effect.' ) ;
1392
1415
captureCommitPhaseError ( nextEffect , error ) ;
@@ -1540,7 +1563,10 @@ function commitBeforeMutationEffects() {
1540
1563
}
1541
1564
}
1542
1565
1543
- function commitMutationEffects ( ) {
1566
+ function commitMutationEffects (
1567
+ root : FiberRoot ,
1568
+ committedExpirationTime : ExpirationTime ,
1569
+ ) {
1544
1570
// TODO: Should probably move the bulk of this function to commitWork.
1545
1571
while ( nextEffect !== null ) {
1546
1572
setCurrentDebugFiberInDEV ( nextEffect ) ;
@@ -1582,12 +1608,12 @@ function commitMutationEffects() {
1582
1608
1583
1609
// Update
1584
1610
const current = nextEffect . alternate ;
1585
- commitWork ( current , nextEffect ) ;
1611
+ commitWork ( root , current , nextEffect , committedExpirationTime ) ;
1586
1612
break ;
1587
1613
}
1588
1614
case Update : {
1589
1615
const current = nextEffect . alternate ;
1590
- commitWork ( current , nextEffect ) ;
1616
+ commitWork ( root , current , nextEffect , committedExpirationTime ) ;
1591
1617
break ;
1592
1618
}
1593
1619
case Deletion : {
@@ -2161,6 +2187,24 @@ function warnIfNotCurrentlyActingUpdatesInDEV(fiber: Fiber): void {
2161
2187
}
2162
2188
}
2163
2189
2190
+ export function restorePendingUpdaters (
2191
+ root : FiberRoot ,
2192
+ expirationTime : ExpirationTime ,
2193
+ ) : void {
2194
+ if ( ! enableUpdaterTracking ) {
2195
+ return ;
2196
+ }
2197
+ const pendingUpdatersMap = root . pendingUpdatersMap ;
2198
+ let updaters = pendingUpdatersMap . get ( expirationTime ) ;
2199
+ if ( updaters == null ) {
2200
+ updaters = new Set ( ) ;
2201
+ pendingUpdatersMap . set ( expirationTime , updaters ) ;
2202
+ }
2203
+ root . memoizedUpdaters . forEach ( schedulingFiber => {
2204
+ ( ( updaters : any ) : Set < Fiber > ) . add ( schedulingFiber ) ;
2205
+ } ) ;
2206
+ }
2207
+
2164
2208
export const warnIfNotCurrentlyActingUpdatesInDev = warnIfNotCurrentlyActingUpdatesInDEV ;
2165
2209
2166
2210
let componentsWithSuspendedDiscreteUpdates = null ;
@@ -2277,42 +2321,58 @@ function schedulePendingInteraction(root, expirationTime) {
2277
2321
2278
2322
function startWorkOnPendingInteraction ( root , expirationTime ) {
2279
2323
// This is called when new work is started on a root.
2280
- if ( ! enableSchedulerTracing ) {
2281
- return ;
2282
- }
2283
2324
2284
- // Determine which interactions this batch of work currently includes, So that
2285
- // we can accurately attribute time spent working on it, And so that cascading
2286
- // work triggered during the render phase will be associated with it.
2287
- const interactions : Set < Interaction > = new Set();
2288
- root.pendingInteractionMap.forEach(
2289
- (scheduledInteractions, scheduledExpirationTime) => {
2325
+ if ( enableUpdaterTracking ) {
2326
+ const memoizedUpdaters : Set < Fiber > = new Set();
2327
+ const pendingUpdatersMap = root.pendingUpdatersMap;
2328
+ pendingUpdatersMap.forEach((updaters, scheduledExpirationTime) => {
2290
2329
if ( scheduledExpirationTime >= expirationTime ) {
2291
- scheduledInteractions . forEach ( interaction =>
2292
- interactions . add ( interaction ) ,
2293
- ) ;
2330
+ pendingUpdatersMap . delete ( scheduledExpirationTime ) ;
2331
+ updaters . forEach ( fiber => memoizedUpdaters . add ( fiber ) ) ;
2294
2332
}
2295
- } ,
2296
- ) ;
2333
+ } ) ;
2297
2334
2298
- // Store the current set of interactions on the FiberRoot for a few reasons:
2299
- // We can re-use it in hot functions like renderRoot() without having to
2300
- // recalculate it. We will also use it in commitWork() to pass to any Profiler
2301
- // onRender () hooks. This also provides DevTools with a way to access it when
2302
- // the onCommitRoot() hook is called.
2303
- root . memoizedInteractions = interactions ;
2335
+ // Store the current set of interactions on the FiberRoot for a few reasons:
2336
+ // We can re-use it in hot functions like renderRoot() without having to
2337
+ // recalculate it. This also provides DevTools with a way to access it when
2338
+ // the onCommitRoot () hook is called.
2339
+ root . memoizedUpdaters = memoizedUpdaters ;
2340
+ }
2304
2341
2305
- if ( interactions . size > 0 ) {
2306
- const subscriber = __subscriberRef . current ;
2307
- if ( subscriber !== null ) {
2308
- const threadID = computeThreadID ( root , expirationTime ) ;
2309
- try {
2310
- subscriber . onWorkStarted ( interactions , threadID ) ;
2311
- } catch ( error ) {
2312
- // If the subscriber throws, rethrow it in a separate task
2313
- scheduleCallback ( ImmediatePriority , ( ) => {
2314
- throw error ;
2315
- } ) ;
2342
+ if ( enableSchedulerTracing ) {
2343
+ // Determine which interactions this batch of work currently includes, So that
2344
+ // we can accurately attribute time spent working on it, And so that cascading
2345
+ // work triggered during the render phase will be associated with it.
2346
+ const interactions : Set < Interaction > = new Set();
2347
+ root.pendingInteractionMap.forEach(
2348
+ (scheduledInteractions, scheduledExpirationTime) => {
2349
+ if ( scheduledExpirationTime >= expirationTime ) {
2350
+ scheduledInteractions . forEach ( interaction =>
2351
+ interactions . add ( interaction ) ,
2352
+ ) ;
2353
+ }
2354
+ } ,
2355
+ ) ;
2356
+
2357
+ // Store the current set of interactions on the FiberRoot for a few reasons:
2358
+ // We can re-use it in hot functions like renderRoot() without having to
2359
+ // recalculate it. We will also use it in commitWork() to pass to any Profiler
2360
+ // onRender() hooks. This also provides DevTools with a way to access it when
2361
+ // the onCommitRoot() hook is called.
2362
+ root . memoizedInteractions = interactions ;
2363
+
2364
+ if ( interactions . size > 0 ) {
2365
+ const subscriber = __subscriberRef . current ;
2366
+ if ( subscriber !== null ) {
2367
+ const threadID = computeThreadID ( root , expirationTime ) ;
2368
+ try {
2369
+ subscriber . onWorkStarted ( interactions , threadID ) ;
2370
+ } catch ( error ) {
2371
+ // If the subscriber throws, rethrow it in a separate task
2372
+ scheduleCallback ( ImmediatePriority , ( ) => {
2373
+ throw error ;
2374
+ } ) ;
2375
+ }
2316
2376
}
2317
2377
}
2318
2378
}
0 commit comments