@@ -257,11 +257,6 @@ let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;
257257// Used for performance tracking.
258258let interruptedBy : Fiber | null = null ;
259259
260- // Do not decrement interaction counts in the event of suspense timeouts.
261- // This would lead to prematurely calling the interaction-complete hook.
262- // Instead we wait until the suspended promise has resolved.
263- let nextRenderIncludesTimedOutPlaceholder : boolean = false ;
264-
265260let stashedWorkInProgressProperties ;
266261let replayUnitOfWork ;
267262let isReplayingFailedUnitOfWork ;
@@ -766,42 +761,40 @@ function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
766761 unhandledError = error ;
767762 }
768763 } finally {
769- if ( ! nextRenderIncludesTimedOutPlaceholder ) {
770- // Clear completed interactions from the pending Map.
771- // Unless the render was suspended or cascading work was scheduled,
772- // In which case– leave pending interactions until the subsequent render.
773- const pendingInteractionMap = root . pendingInteractionMap ;
774- pendingInteractionMap . forEach (
775- ( scheduledInteractions , scheduledExpirationTime ) => {
776- // Only decrement the pending interaction count if we're done.
777- // If there's still work at the current priority,
778- // That indicates that we are waiting for suspense data.
779- if (
780- earliestRemainingTimeAfterCommit === NoWork ||
781- scheduledExpirationTime < earliestRemainingTimeAfterCommit
782- ) {
783- pendingInteractionMap . delete ( scheduledExpirationTime ) ;
784-
785- scheduledInteractions . forEach ( interaction => {
786- interaction . __count -- ;
787-
788- if ( subscriber !== null && interaction . __count === 0 ) {
789- try {
790- subscriber . onInteractionScheduledWorkCompleted ( interaction ) ;
791- } catch ( error ) {
792- // It's not safe for commitRoot() to throw.
793- // Store the error for now and we'll re-throw in finishRendering().
794- if ( ! hasUnhandledError ) {
795- hasUnhandledError = true ;
796- unhandledError = error ;
797- }
764+ // Clear completed interactions from the pending Map.
765+ // Unless the render was suspended or cascading work was scheduled,
766+ // In which case– leave pending interactions until the subsequent render.
767+ const pendingInteractionMap = root . pendingInteractionMap ;
768+ pendingInteractionMap . forEach (
769+ ( scheduledInteractions , scheduledExpirationTime ) => {
770+ // Only decrement the pending interaction count if we're done.
771+ // If there's still work at the current priority,
772+ // That indicates that we are waiting for suspense data.
773+ if (
774+ earliestRemainingTimeAfterCommit === NoWork ||
775+ scheduledExpirationTime < earliestRemainingTimeAfterCommit
776+ ) {
777+ pendingInteractionMap . delete ( scheduledExpirationTime ) ;
778+
779+ scheduledInteractions . forEach ( interaction => {
780+ interaction . __count -- ;
781+
782+ if ( subscriber !== null && interaction . __count === 0 ) {
783+ try {
784+ subscriber . onInteractionScheduledWorkCompleted ( interaction ) ;
785+ } catch ( error ) {
786+ // It's not safe for commitRoot() to throw.
787+ // Store the error for now and we'll re-throw in finishRendering().
788+ if ( ! hasUnhandledError ) {
789+ hasUnhandledError = true ;
790+ unhandledError = error ;
798791 }
799792 }
800- } ) ;
801- }
802- } ,
803- ) ;
804- }
793+ }
794+ } ) ;
795+ }
796+ } ,
797+ ) ;
805798 }
806799 }
807800}
@@ -1173,11 +1166,6 @@ function renderRoot(
11731166 root . pendingCommitExpirationTime = NoWork ;
11741167
11751168 if ( enableSchedulerTracing ) {
1176- // Reset this flag once we start rendering a new root or at a new priority.
1177- // This might indicate that suspended work has completed.
1178- // If not, the flag will be reset.
1179- nextRenderIncludesTimedOutPlaceholder = false ;
1180-
11811169 // Determine which interactions this batch of work currently includes,
11821170 // So that we can accurately attribute time spent working on it,
11831171 // And so that cascading work triggered during the render phase will be associated with it.
@@ -1388,9 +1376,6 @@ function renderRoot(
13881376
13891377 if ( enableSuspense && ! isExpired && nextLatestAbsoluteTimeoutMs !== - 1 ) {
13901378 // The tree was suspended.
1391- if ( enableSchedulerTracing ) {
1392- nextRenderIncludesTimedOutPlaceholder = true ;
1393- }
13941379 const suspendedExpirationTime = expirationTime ;
13951380 markSuspendedPriorityLevel ( root , suspendedExpirationTime ) ;
13961381
@@ -1597,6 +1582,13 @@ function retrySuspendedRoot(
15971582 markPendingPriorityLevel ( root , retryTime ) ;
15981583 }
15991584
1585+ // TODO: If the placeholder fiber has already rendered the primary children
1586+ // without suspending (that is, all of the promises have already resolved),
1587+ // we should not trigger another update here. One case this happens is when
1588+ // we are in sync mode and a single promise is thrown both on initial render
1589+ // and on update; we attach two .then(retrySuspendedRoot) callbacks and each
1590+ // one performs Sync work, rerendering the Placeholder.
1591+
16001592 if ( ( fiber . mode & ConcurrentMode ) !== NoContext ) {
16011593 if ( root === nextRoot && nextRenderExpirationTime === suspendedTime ) {
16021594 // Received a ping at the same priority level at which we're currently
@@ -1614,6 +1606,15 @@ function retrySuspendedRoot(
16141606}
16151607
16161608function scheduleWorkToRoot ( fiber : Fiber , expirationTime ) : FiberRoot | null {
1609+ recordScheduleUpdate ( ) ;
1610+
1611+ if ( __DEV__ ) {
1612+ if ( fiber . tag === ClassComponent || fiber . tag === ClassComponentLazy ) {
1613+ const instance = fiber . stateNode ;
1614+ warnAboutInvalidUpdates ( instance ) ;
1615+ }
1616+ }
1617+
16171618 // Update the source fiber's expiration time
16181619 if (
16191620 fiber . expirationTime === NoWork ||
@@ -1631,57 +1632,47 @@ function scheduleWorkToRoot(fiber: Fiber, expirationTime): FiberRoot | null {
16311632 }
16321633 // Walk the parent path to the root and update the child expiration time.
16331634 let node = fiber . return ;
1635+ let root = null ;
16341636 if ( node === null && fiber . tag === HostRoot ) {
1635- return fiber . stateNode ;
1636- }
1637- while ( node !== null ) {
1638- alternate = node . alternate ;
1639- if (
1640- node . childExpirationTime === NoWork ||
1641- node . childExpirationTime > expirationTime
1642- ) {
1643- node . childExpirationTime = expirationTime ;
1637+ root = fiber . stateNode ;
1638+ } else {
1639+ while ( node !== null ) {
1640+ alternate = node . alternate ;
16441641 if (
1642+ node . childExpirationTime === NoWork ||
1643+ node . childExpirationTime > expirationTime
1644+ ) {
1645+ node . childExpirationTime = expirationTime ;
1646+ if (
1647+ alternate !== null &&
1648+ ( alternate . childExpirationTime === NoWork ||
1649+ alternate . childExpirationTime > expirationTime )
1650+ ) {
1651+ alternate . childExpirationTime = expirationTime ;
1652+ }
1653+ } else if (
16451654 alternate !== null &&
16461655 ( alternate . childExpirationTime === NoWork ||
16471656 alternate . childExpirationTime > expirationTime )
16481657 ) {
16491658 alternate . childExpirationTime = expirationTime ;
16501659 }
1651- } else if (
1652- alternate !== null &&
1653- ( alternate . childExpirationTime === NoWork ||
1654- alternate . childExpirationTime > expirationTime )
1655- ) {
1656- alternate . childExpirationTime = expirationTime ;
1657- }
1658- if ( node . return === null && node . tag === HostRoot ) {
1659- return node . stateNode ;
1660- }
1661- node = node . return ;
1662- }
1663- return null ;
1664- }
1665-
1666- function scheduleWork ( fiber : Fiber , expirationTime : ExpirationTime ) {
1667- recordScheduleUpdate ( ) ;
1668-
1669- if ( __DEV__ ) {
1670- if ( fiber . tag === ClassComponent || fiber . tag === ClassComponentLazy ) {
1671- const instance = fiber . stateNode ;
1672- warnAboutInvalidUpdates ( instance ) ;
1660+ if ( node . return === null && node . tag === HostRoot ) {
1661+ root = node . stateNode ;
1662+ break ;
1663+ }
1664+ node = node . return ;
16731665 }
16741666 }
16751667
1676- const root = scheduleWorkToRoot ( fiber , expirationTime ) ;
16771668 if ( root === null ) {
16781669 if (
16791670 __DEV__ &&
16801671 ( fiber . tag === ClassComponent || fiber . tag === ClassComponentLazy )
16811672 ) {
16821673 warnAboutUpdateOnUnmounted ( fiber ) ;
16831674 }
1684- return ;
1675+ return null ;
16851676 }
16861677
16871678 if ( enableSchedulerTracing ) {
@@ -1718,6 +1709,15 @@ function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
17181709 }
17191710 }
17201711
1712+ return root ;
1713+ }
1714+
1715+ function scheduleWork ( fiber : Fiber , expirationTime : ExpirationTime ) {
1716+ const root = scheduleWorkToRoot ( fiber , expirationTime ) ;
1717+ if ( root === null ) {
1718+ return ;
1719+ }
1720+
17211721 if (
17221722 ! isWorking &&
17231723 nextRenderExpirationTime !== NoWork &&
@@ -1904,15 +1904,7 @@ function onTimeout(root, finishedWork, suspendedExpirationTime) {
19041904 // because we're at the top of a timer event.
19051905 recomputeCurrentRendererTime ( ) ;
19061906 currentSchedulerTime = currentRendererTime ;
1907-
1908- if ( enableSchedulerTracing ) {
1909- // Don't update pending interaction counts for suspense timeouts,
1910- // Because we know we still need to do more work in this case.
1911- nextRenderIncludesTimedOutPlaceholder = true ;
1912- flushRoot ( root , suspendedExpirationTime ) ;
1913- } else {
1914- flushRoot ( root , suspendedExpirationTime ) ;
1915- }
1907+ flushRoot ( root , suspendedExpirationTime ) ;
19161908 }
19171909}
19181910
@@ -2480,12 +2472,6 @@ function flushControlled(fn: () => mixed): void {
24802472 }
24812473}
24822474
2483- function captureWillSyncRenderPlaceholder ( ) {
2484- if ( enableSchedulerTracing ) {
2485- nextRenderIncludesTimedOutPlaceholder = true ;
2486- }
2487- }
2488-
24892475export {
24902476 requestCurrentTime ,
24912477 computeExpirationForFiber,
@@ -2508,5 +2494,4 @@ export {
25082494 interactiveUpdates,
25092495 flushInteractiveUpdates,
25102496 computeUniqueAsyncExpiration,
2511- captureWillSyncRenderPlaceholder,
25122497} ;
0 commit comments