@@ -171,6 +171,7 @@ import {
171171 getCurrentUpdateLanePriority ,
172172 markStarvedLanesAsExpired ,
173173 getLanesToRetrySynchronouslyOnError ,
174+ getMostRecentEventTime ,
174175 markRootUpdated ,
175176 markRootSuspended as markRootSuspended_dontCallThisOneDirectly ,
176177 markRootPinged ,
@@ -294,8 +295,6 @@ const subtreeRenderLanesCursor: StackCursor<Lanes> = createCursor(NoLanes);
294295let workInProgressRootExitStatus: RootExitStatus = RootIncomplete;
295296// A fatal error, if one is thrown
296297let workInProgressRootFatalError: mixed = null;
297- // Most recent event time among processed updates during this render.
298- let workInProgressRootLatestProcessedEventTime: number = NoTimestamp;
299298let workInProgressRootLatestSuspenseTimeout: number = NoTimestamp;
300299let workInProgressRootCanSuspendUsingConfig: null | SuspenseConfig = null;
301300// "Included" lanes refer to lanes that were worked on during this render. It's
@@ -540,6 +539,35 @@ export function scheduleUpdateOnFiber(
540539 return null ;
541540 }
542541
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+
543571 // TODO: requestUpdateLanePriority also reads the priority. Pass the
544572 // priority as an argument to that function and this one.
545573 const priorityLevel = getCurrentPriorityLevel ( ) ;
@@ -605,82 +633,47 @@ export function scheduleUpdateOnFiber(
605633// e.g. retrying a Suspense boundary isn't an update, but it does schedule work
606634// on a fiber.
607635function markUpdateLaneFromFiberToRoot (
608- fiber : Fiber ,
636+ sourceFiber : Fiber ,
609637 lane : Lane ,
610638) : FiberRoot | null {
611639 // 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 ;
614642 if ( alternate !== null ) {
615643 alternate . lanes = mergeLanes ( alternate . lanes , lane ) ;
616644 }
617645 if (__DEV__) {
618646 if (
619647 alternate === null &&
620- ( fiber . effectTag & ( Placement | Hydrating ) ) !== NoEffect
648+ ( sourceFiber . effectTag & ( Placement | Hydrating ) ) !== NoEffect
621649 ) {
622- warnAboutUpdateOnNotYetMountedFiberInDEV ( fiber ) ;
650+ warnAboutUpdateOnNotYetMountedFiberInDEV ( sourceFiber ) ;
623651 }
624652 }
625653 // 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 {
633662 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 ) ;
639665 }
640666 }
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;
650667 }
668+ node = parent ;
669+ parent = parent . return ;
651670 }
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 ;
681676 }
682-
683- return root ;
684677}
685678
686679// 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) {
944937 break ;
945938 }
946939
940+ const mostRecentEventTime = getMostRecentEventTime ( root , lanes ) ;
947941 let msUntilTimeout ;
948942 if ( workInProgressRootLatestSuspenseTimeout !== NoTimestamp ) {
949943 // We have processed a suspense config whose expiration time we
950944 // can use as the timeout.
951945 msUntilTimeout = workInProgressRootLatestSuspenseTimeout - now ( ) ;
952- } else if ( workInProgressRootLatestProcessedEventTime === NoTimestamp ) {
946+ } else if ( mostRecentEventTime === NoTimestamp ) {
953947 // This should never normally happen because only new updates
954948 // cause delayed states, so we should have processed something.
955949 // However, this could also happen in an offscreen tree.
956950 msUntilTimeout = 0 ;
957951 } else {
958952 // If we didn't process a suspense config, compute a JND based on
959953 // the amount of time elapsed since the most recent event time.
960- const eventTimeMs = workInProgressRootLatestProcessedEventTime ;
954+ const eventTimeMs = mostRecentEventTime ;
961955 const timeElapsedMs = now ( ) - eventTimeMs ;
962956 msUntilTimeout = jnd ( timeElapsedMs ) - timeElapsedMs ;
963957 }
@@ -980,18 +974,19 @@ function finishConcurrentRender(root, finishedWork, exitStatus, lanes) {
980974 }
981975 case RootCompleted : {
982976 // The work completed. Ready to commit.
977+ const mostRecentEventTime = getMostRecentEventTime ( root , lanes ) ;
983978 if (
984979 // do not delay if we're inside an act() scope
985980 ! shouldForceFlushFallbacksInDEV ( ) &&
986- workInProgressRootLatestProcessedEventTime !== NoTimestamp &&
981+ mostRecentEventTime !== NoTimestamp &&
987982 workInProgressRootCanSuspendUsingConfig !== null
988983 ) {
989984 // If we have exceeded the minimum loading delay, which probably
990985 // means we have shown a spinner already, we might have to suspend
991986 // a bit longer to ensure that the spinner is shown for
992987 // enough time.
993988 const msUntilTimeout = computeMsUntilSuspenseLoadingDelay (
994- workInProgressRootLatestProcessedEventTime ,
989+ mostRecentEventTime ,
995990 workInProgressRootCanSuspendUsingConfig ,
996991 ) ;
997992 if ( msUntilTimeout > 10 ) {
@@ -1329,7 +1324,6 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
13291324 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes ;
13301325 workInProgressRootExitStatus = RootIncomplete ;
13311326 workInProgressRootFatalError = null ;
1332- workInProgressRootLatestProcessedEventTime = NoTimestamp ;
13331327 workInProgressRootLatestSuspenseTimeout = NoTimestamp ;
13341328 workInProgressRootCanSuspendUsingConfig = null ;
13351329 workInProgressRootSkippedLanes = NoLanes ;
@@ -1447,11 +1441,6 @@ export function markRenderEventTimeAndConfig(
14471441 eventTime : number ,
14481442 suspenseConfig : null | SuspenseConfig ,
14491443) : void {
1450- // Track the most recent event time of all updates processed in this batch.
1451- if ( workInProgressRootLatestProcessedEventTime < eventTime ) {
1452- workInProgressRootLatestProcessedEventTime = eventTime ;
1453- }
1454-
14551444 // Track the largest/latest timeout deadline in this batch.
14561445 // TODO: If there are two transitions in the same batch, shouldn't we
14571446 // choose the smaller one? Maybe this is because when an intermediate
@@ -2908,6 +2897,7 @@ function captureCommitPhaseErrorOnRoot(
29082897 const eventTime = requestEventTime ( ) ;
29092898 const root = markUpdateLaneFromFiberToRoot ( rootFiber , ( SyncLane : Lane ) ) ;
29102899 if ( root !== null ) {
2900+ markRootUpdated ( root , SyncLane , eventTime ) ;
29112901 ensureRootIsScheduled ( root , eventTime ) ;
29122902 schedulePendingInteractions ( root , SyncLane ) ;
29132903 }
@@ -2944,6 +2934,7 @@ export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {
29442934 const eventTime = requestEventTime ( ) ;
29452935 const root = markUpdateLaneFromFiberToRoot ( fiber , ( SyncLane : Lane ) ) ;
29462936 if ( root !== null ) {
2937+ markRootUpdated ( root , SyncLane , eventTime ) ;
29472938 ensureRootIsScheduled ( root , eventTime ) ;
29482939 schedulePendingInteractions ( root , SyncLane ) ;
29492940 }
@@ -3016,6 +3007,7 @@ function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
30163007 const eventTime = requestEventTime ( ) ;
30173008 const root = markUpdateLaneFromFiberToRoot ( boundaryFiber , retryLane ) ;
30183009 if ( root !== null ) {
3010+ markRootUpdated ( root , retryLane , eventTime ) ;
30193011 ensureRootIsScheduled ( root , eventTime ) ;
30203012 schedulePendingInteractions ( root , retryLane ) ;
30213013 }
0 commit comments