@@ -68,6 +68,7 @@ import {
6868 markRootSuspendedAtTime ,
6969 markRootFinishedAtTime ,
7070 markRootUpdatedAtTime ,
71+ markRootExpiredAtTime ,
7172} from './ReactFiberRoot' ;
7273import {
7374 NoMode ,
@@ -521,10 +522,14 @@ function getNextRootExpirationTimeToWorkOn(root: FiberRoot): ExpirationTime {
521522 // Determines the next expiration time that the root should render, taking
522523 // into account levels that may be suspended, or levels that may have
523524 // received a ping.
524- //
525+
526+ const lastExpiredTime = root . lastExpiredTime ;
527+ if ( lastExpiredTime !== NoWork ) {
528+ return lastExpiredTime ;
529+ }
530+
525531 // "Pending" refers to any update that hasn't committed yet, including if it
526532 // suspended. The "suspended" range is therefore a subset.
527-
528533 const firstPendingTime = root . firstPendingTime ;
529534 if ( ! isRootSuspendedAtTime ( root , firstPendingTime ) ) {
530535 // The highest priority pending time is not suspended. Let's work on that.
@@ -547,6 +552,15 @@ function getNextRootExpirationTimeToWorkOn(root: FiberRoot): ExpirationTime {
547552// the next level that the root has work on. This function is called on every
548553// update, and right before exiting a task.
549554function ensureRootIsScheduled ( root : FiberRoot ) {
555+ const lastExpiredTime = root . lastExpiredTime ;
556+ if ( lastExpiredTime !== NoWork ) {
557+ // Special case: Expired work should flush synchronously.
558+ scheduleSyncCallback (
559+ performSyncWorkOnRoot . bind ( null , root , lastExpiredTime ) ,
560+ ) ;
561+ return ;
562+ }
563+
550564 const expirationTime = getNextRootExpirationTimeToWorkOn ( root ) ;
551565 const existingCallbackNode = root . callbackNode ;
552566 if ( expirationTime === NoWork ) {
@@ -621,20 +635,16 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
621635 // event time. The next update will compute a new event time.
622636 currentEventTime = NoWork ;
623637
638+ if ( didTimeout ) {
639+ // An async update expired.
640+ const currentTime = requestCurrentTime ( ) ;
641+ markRootExpiredAtTime ( root , currentTime ) ;
642+ }
643+
624644 // Determine the next expiration time to work on, using the fields stored
625645 // on the root.
626- let expirationTime = getNextRootExpirationTimeToWorkOn ( root ) ;
646+ const expirationTime = getNextRootExpirationTimeToWorkOn ( root ) ;
627647 if ( expirationTime !== NoWork ) {
628- if ( didTimeout ) {
629- // An async update expired. There may be other expired updates on
630- // this root.
631- const currentTime = requestCurrentTime ( ) ;
632- if ( currentTime < expirationTime ) {
633- // Render all the expired work in a single batch.
634- expirationTime = currentTime ;
635- }
636- }
637-
638648 const originalCallbackNode = root . callbackNode ;
639649 try {
640650 renderRoot ( root , expirationTime , didTimeout ) ;
@@ -673,7 +683,9 @@ export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {
673683 'means you attempted to commit from inside a lifecycle method.' ,
674684 ) ;
675685 }
676- performSyncWorkOnRoot ( root , expirationTime ) ;
686+ markRootExpiredAtTime ( root , expirationTime ) ;
687+ ensureRootIsScheduled ( root ) ;
688+ flushSyncCallbackQueue ( ) ;
677689}
678690
679691export function flushDiscreteUpdates ( ) {
@@ -741,9 +753,8 @@ function flushPendingDiscreteUpdates() {
741753 const roots = rootsWithPendingDiscreteUpdates ;
742754 rootsWithPendingDiscreteUpdates = null ;
743755 roots . forEach ( ( expirationTime , root ) => {
744- scheduleSyncCallback (
745- performSyncWorkOnRoot . bind ( null , root , expirationTime ) ,
746- ) ;
756+ markRootExpiredAtTime ( root , expirationTime ) ;
757+ ensureRootIsScheduled ( root ) ;
747758 } ) ;
748759 // Now flush the immediate queue.
749760 flushSyncCallbackQueue ( ) ;
@@ -1032,7 +1043,7 @@ function renderRoot(
10321043 // synchronously, to see if the error goes away. If there are lower
10331044 // priority updates, let's include those, too, in case they fix the
10341045 // inconsistency. Render at Idle to include all updates.
1035- performSyncWorkOnRoot ( root , Idle ) ;
1046+ markRootExpiredAtTime ( root , Idle ) ;
10361047 return ;
10371048 }
10381049 // Commit the root in its errored state.
0 commit comments