@@ -171,6 +171,7 @@ import {
171
171
getCurrentUpdateLanePriority ,
172
172
markStarvedLanesAsExpired ,
173
173
getLanesToRetrySynchronouslyOnError ,
174
+ getMostRecentEventTime ,
174
175
markRootUpdated ,
175
176
markRootSuspended as markRootSuspended_dontCallThisOneDirectly ,
176
177
markRootPinged ,
@@ -294,8 +295,6 @@ const subtreeRenderLanesCursor: StackCursor<Lanes> = createCursor(NoLanes);
294
295
let workInProgressRootExitStatus: RootExitStatus = RootIncomplete;
295
296
// A fatal error, if one is thrown
296
297
let workInProgressRootFatalError: mixed = null;
297
- // Most recent event time among processed updates during this render.
298
- let workInProgressRootLatestProcessedEventTime: number = NoTimestamp;
299
298
let workInProgressRootLatestSuspenseTimeout: number = NoTimestamp;
300
299
let workInProgressRootCanSuspendUsingConfig: null | SuspenseConfig = null;
301
300
// "Included" lanes refer to lanes that were worked on during this render. It's
@@ -540,6 +539,35 @@ export function scheduleUpdateOnFiber(
540
539
return null ;
541
540
}
542
541
542
+ // Mark that the root has a pending update.
543
+ markRootUpdated(root, lane, eventTime);
544
+
545
+ if (root === workInProgressRoot) {
546
+ // Received an update to a tree that's in the middle of rendering. Mark
547
+ // that there was an interleaved update work on this root. Unless the
548
+ // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render
549
+ // phase update. In that case, we don't treat render phase updates as if
550
+ // they were interleaved, for backwards compat reasons.
551
+ if (
552
+ deferRenderPhaseUpdateToNextBatch ||
553
+ ( executionContext & RenderContext ) === NoContext
554
+ ) {
555
+ workInProgressRootUpdatedLanes = mergeLanes (
556
+ workInProgressRootUpdatedLanes ,
557
+ lane ,
558
+ ) ;
559
+ }
560
+ if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
561
+ // The root already suspended with a delay, which means this render
562
+ // definitely won't finish. Since we have a new update, let's mark it as
563
+ // suspended now, right before marking the incoming update. This has the
564
+ // effect of interrupting the current render and switching to the update.
565
+ // TODO: Make sure this doesn't override pings that happen while we've
566
+ // already started rendering.
567
+ markRootSuspended ( root , workInProgressRootRenderLanes ) ;
568
+ }
569
+ }
570
+
543
571
// TODO: requestUpdateLanePriority also reads the priority. Pass the
544
572
// priority as an argument to that function and this one.
545
573
const priorityLevel = getCurrentPriorityLevel ( ) ;
@@ -605,82 +633,47 @@ export function scheduleUpdateOnFiber(
605
633
// e.g. retrying a Suspense boundary isn't an update, but it does schedule work
606
634
// on a fiber.
607
635
function markUpdateLaneFromFiberToRoot (
608
- fiber : Fiber ,
636
+ sourceFiber : Fiber ,
609
637
lane : Lane ,
610
638
) : FiberRoot | null {
611
639
// Update the source fiber's lanes
612
- fiber . lanes = mergeLanes ( fiber . lanes , lane ) ;
613
- let alternate = fiber . alternate ;
640
+ sourceFiber . lanes = mergeLanes ( sourceFiber . lanes , lane ) ;
641
+ let alternate = sourceFiber . alternate ;
614
642
if ( alternate !== null ) {
615
643
alternate . lanes = mergeLanes ( alternate . lanes , lane ) ;
616
644
}
617
645
if (__DEV__) {
618
646
if (
619
647
alternate === null &&
620
- ( fiber . effectTag & ( Placement | Hydrating ) ) !== NoEffect
648
+ ( sourceFiber . effectTag & ( Placement | Hydrating ) ) !== NoEffect
621
649
) {
622
- warnAboutUpdateOnNotYetMountedFiberInDEV ( fiber ) ;
650
+ warnAboutUpdateOnNotYetMountedFiberInDEV ( sourceFiber ) ;
623
651
}
624
652
}
625
653
// Walk the parent path to the root and update the child expiration time.
626
- let node = fiber . return ;
627
- let root = null ;
628
- if ( node === null && fiber . tag === HostRoot ) {
629
- root = fiber . stateNode ;
630
- } else {
631
- while ( node !== null ) {
632
- alternate = node . alternate ;
654
+ let node = sourceFiber ;
655
+ let parent = sourceFiber . return ;
656
+ while ( parent !== null ) {
657
+ parent . childLanes = mergeLanes ( parent . childLanes , lane ) ;
658
+ alternate = parent . alternate ;
659
+ if ( alternate !== null ) {
660
+ alternate . childLanes = mergeLanes ( alternate . childLanes , lane ) ;
661
+ } else {
633
662
if ( __DEV__ ) {
634
- if (
635
- alternate === null &&
636
- ( node . effectTag & ( Placement | Hydrating ) ) !== NoEffect
637
- ) {
638
- warnAboutUpdateOnNotYetMountedFiberInDEV ( fiber ) ;
663
+ if ( ( parent . effectTag & ( Placement | Hydrating ) ) !== NoEffect ) {
664
+ warnAboutUpdateOnNotYetMountedFiberInDEV ( sourceFiber ) ;
639
665
}
640
666
}
641
- node . childLanes = mergeLanes ( node . childLanes , lane ) ;
642
- if ( alternate !== null ) {
643
- alternate . childLanes = mergeLanes ( alternate . childLanes , lane ) ;
644
- }
645
- if (node.return === null && node . tag === HostRoot ) {
646
- root = node . stateNode ;
647
- break ;
648
- }
649
- node = node.return;
650
667
}
668
+ node = parent ;
669
+ parent = parent . return ;
651
670
}
652
-
653
- if ( root !== null ) {
654
- // Mark that the root has a pending update.
655
- markRootUpdated ( root , lane ) ;
656
- if ( workInProgressRoot === root ) {
657
- // Received an update to a tree that's in the middle of rendering. Mark
658
- // that there was an interleaved update work on this root. Unless the
659
- // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render
660
- // phase update. In that case, we don't treat render phase updates as if
661
- // they were interleaved, for backwards compat reasons.
662
- if (
663
- deferRenderPhaseUpdateToNextBatch ||
664
- ( executionContext & RenderContext ) === NoContext
665
- ) {
666
- workInProgressRootUpdatedLanes = mergeLanes (
667
- workInProgressRootUpdatedLanes ,
668
- lane ,
669
- ) ;
670
- }
671
- if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
672
- // The root already suspended with a delay, which means this render
673
- // definitely won't finish. Since we have a new update, let's mark it as
674
- // suspended now, right before marking the incoming update. This has the
675
- // effect of interrupting the current render and switching to the update.
676
- // TODO: Make sure this doesn't override pings that happen while we've
677
- // already started rendering.
678
- markRootSuspended ( root , workInProgressRootRenderLanes ) ;
679
- }
680
- }
671
+ if ( node . tag === HostRoot ) {
672
+ const root : FiberRoot = node . stateNode ;
673
+ return root ;
674
+ } else {
675
+ return null ;
681
676
}
682
-
683
- return root ;
684
677
}
685
678
686
679
// Use this function to schedule a task for a root. There's only one task per
@@ -944,20 +937,21 @@ function finishConcurrentRender(root, finishedWork, exitStatus, lanes) {
944
937
break ;
945
938
}
946
939
940
+ const mostRecentEventTime = getMostRecentEventTime ( root , lanes ) ;
947
941
let msUntilTimeout ;
948
942
if ( workInProgressRootLatestSuspenseTimeout !== NoTimestamp ) {
949
943
// We have processed a suspense config whose expiration time we
950
944
// can use as the timeout.
951
945
msUntilTimeout = workInProgressRootLatestSuspenseTimeout - now ( ) ;
952
- } else if ( workInProgressRootLatestProcessedEventTime === NoTimestamp ) {
946
+ } else if ( mostRecentEventTime === NoTimestamp ) {
953
947
// This should never normally happen because only new updates
954
948
// cause delayed states, so we should have processed something.
955
949
// However, this could also happen in an offscreen tree.
956
950
msUntilTimeout = 0 ;
957
951
} else {
958
952
// If we didn't process a suspense config, compute a JND based on
959
953
// the amount of time elapsed since the most recent event time.
960
- const eventTimeMs = workInProgressRootLatestProcessedEventTime ;
954
+ const eventTimeMs = mostRecentEventTime ;
961
955
const timeElapsedMs = now ( ) - eventTimeMs ;
962
956
msUntilTimeout = jnd ( timeElapsedMs ) - timeElapsedMs ;
963
957
}
@@ -980,18 +974,19 @@ function finishConcurrentRender(root, finishedWork, exitStatus, lanes) {
980
974
}
981
975
case RootCompleted : {
982
976
// The work completed. Ready to commit.
977
+ const mostRecentEventTime = getMostRecentEventTime ( root , lanes ) ;
983
978
if (
984
979
// do not delay if we're inside an act() scope
985
980
! shouldForceFlushFallbacksInDEV ( ) &&
986
- workInProgressRootLatestProcessedEventTime !== NoTimestamp &&
981
+ mostRecentEventTime !== NoTimestamp &&
987
982
workInProgressRootCanSuspendUsingConfig !== null
988
983
) {
989
984
// If we have exceeded the minimum loading delay, which probably
990
985
// means we have shown a spinner already, we might have to suspend
991
986
// a bit longer to ensure that the spinner is shown for
992
987
// enough time.
993
988
const msUntilTimeout = computeMsUntilSuspenseLoadingDelay (
994
- workInProgressRootLatestProcessedEventTime ,
989
+ mostRecentEventTime ,
995
990
workInProgressRootCanSuspendUsingConfig ,
996
991
) ;
997
992
if ( msUntilTimeout > 10 ) {
@@ -1329,7 +1324,6 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
1329
1324
workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes ;
1330
1325
workInProgressRootExitStatus = RootIncomplete ;
1331
1326
workInProgressRootFatalError = null ;
1332
- workInProgressRootLatestProcessedEventTime = NoTimestamp ;
1333
1327
workInProgressRootLatestSuspenseTimeout = NoTimestamp ;
1334
1328
workInProgressRootCanSuspendUsingConfig = null ;
1335
1329
workInProgressRootSkippedLanes = NoLanes ;
@@ -1447,11 +1441,6 @@ export function markRenderEventTimeAndConfig(
1447
1441
eventTime : number ,
1448
1442
suspenseConfig : null | SuspenseConfig ,
1449
1443
) : void {
1450
- // Track the most recent event time of all updates processed in this batch.
1451
- if ( workInProgressRootLatestProcessedEventTime < eventTime ) {
1452
- workInProgressRootLatestProcessedEventTime = eventTime ;
1453
- }
1454
-
1455
1444
// Track the largest/latest timeout deadline in this batch.
1456
1445
// TODO: If there are two transitions in the same batch, shouldn't we
1457
1446
// choose the smaller one? Maybe this is because when an intermediate
@@ -2908,6 +2897,7 @@ function captureCommitPhaseErrorOnRoot(
2908
2897
const eventTime = requestEventTime ( ) ;
2909
2898
const root = markUpdateLaneFromFiberToRoot ( rootFiber , ( SyncLane : Lane ) ) ;
2910
2899
if ( root !== null ) {
2900
+ markRootUpdated ( root , SyncLane , eventTime ) ;
2911
2901
ensureRootIsScheduled ( root , eventTime ) ;
2912
2902
schedulePendingInteractions ( root , SyncLane ) ;
2913
2903
}
@@ -2944,6 +2934,7 @@ export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {
2944
2934
const eventTime = requestEventTime ( ) ;
2945
2935
const root = markUpdateLaneFromFiberToRoot ( fiber , ( SyncLane : Lane ) ) ;
2946
2936
if ( root !== null ) {
2937
+ markRootUpdated ( root , SyncLane , eventTime ) ;
2947
2938
ensureRootIsScheduled ( root , eventTime ) ;
2948
2939
schedulePendingInteractions ( root , SyncLane ) ;
2949
2940
}
@@ -3016,6 +3007,7 @@ function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
3016
3007
const eventTime = requestEventTime ( ) ;
3017
3008
const root = markUpdateLaneFromFiberToRoot ( boundaryFiber , retryLane ) ;
3018
3009
if ( root !== null ) {
3010
+ markRootUpdated ( root , retryLane , eventTime ) ;
3019
3011
ensureRootIsScheduled ( root , eventTime ) ;
3020
3012
schedulePendingInteractions ( root , retryLane ) ;
3021
3013
}
0 commit comments