@@ -125,13 +125,8 @@ import {
125
125
computeAsyncExpiration ,
126
126
computeInteractiveExpiration ,
127
127
} from './ReactFiberExpirationTime' ;
128
- import { ConcurrentMode , ProfileMode , NoContext } from './ReactTypeOfMode' ;
129
- import {
130
- enqueueUpdate ,
131
- resetCurrentlyProcessingQueue ,
132
- ForceUpdate ,
133
- createUpdate ,
134
- } from './ReactUpdateQueue' ;
128
+ import { ConcurrentMode , ProfileMode } from './ReactTypeOfMode' ;
129
+ import { enqueueUpdate , resetCurrentlyProcessingQueue } from './ReactUpdateQueue' ;
135
130
import { createCapturedValue } from './ReactCapturedValue' ;
136
131
import {
137
132
isContextProvider as isLegacyContextProvider ,
@@ -1646,62 +1641,62 @@ function renderDidError() {
1646
1641
nextRenderDidError = true ;
1647
1642
}
1648
1643
1649
- function retrySuspendedRoot (
1644
+ function pingSuspendedRoot (
1650
1645
root : FiberRoot ,
1651
- boundaryFiber : Fiber ,
1652
- sourceFiber : Fiber ,
1653
- suspendedTime : ExpirationTime ,
1646
+ thenable : Thenable ,
1647
+ pingTime : ExpirationTime ,
1654
1648
) {
1655
- let retryTime ;
1649
+ // A promise that previously suspended React from committing has resolved.
1650
+ // If React is still suspended, try again at the previous level (pingTime).
1656
1651
1657
- if ( isPriorityLevelSuspended ( root , suspendedTime ) ) {
1658
- // Ping at the original level
1659
- retryTime = suspendedTime ;
1652
+ const pingCache = root . pingCache ;
1653
+ if ( pingCache !== null ) {
1654
+ // The thenable resolved, so we no longer need to memoize, because it will
1655
+ // never be thrown again.
1656
+ pingCache . delete ( thenable ) ;
1657
+ }
1660
1658
1661
- markPingedPriorityLevel ( root , retryTime ) ;
1659
+ if ( nextRoot !== null && nextRenderExpirationTime === pingTime ) {
1660
+ // Received a ping at the same priority level at which we're currently
1661
+ // rendering. Restart from the root.
1662
+ nextRoot = null ;
1662
1663
} else {
1663
- // Suspense already timed out. Compute a new expiration time
1664
- const currentTime = requestCurrentTime ( ) ;
1665
- retryTime = computeExpirationForFiber ( currentTime , boundaryFiber ) ;
1666
- markPendingPriorityLevel ( root , retryTime ) ;
1664
+ // Confirm that the root is still suspended at this level. Otherwise exit.
1665
+ if ( isPriorityLevelSuspended ( root , pingTime ) ) {
1666
+ // Ping at the original level
1667
+ markPingedPriorityLevel ( root , pingTime ) ;
1668
+ const rootExpirationTime = root . expirationTime ;
1669
+ if ( rootExpirationTime !== NoWork ) {
1670
+ requestWork ( root , rootExpirationTime ) ;
1671
+ }
1672
+ }
1667
1673
}
1674
+ }
1668
1675
1669
- // TODO: If the suspense fiber has already rendered the primary children
1670
- // without suspending (that is, all of the promises have already resolved),
1671
- // we should not trigger another update here. One case this happens is when
1672
- // we are in sync mode and a single promise is thrown both on initial render
1673
- // and on update; we attach two .then(retrySuspendedRoot) callbacks and each
1674
- // one performs Sync work, rerendering the Suspense.
1676
+ function retryTimedOutBoundary ( boundaryFiber : Fiber , thenable : Thenable ) {
1677
+ // The boundary fiber (a Suspense component) previously timed out and was
1678
+ // rendered in its fallback state. One of the promises that suspended it has
1679
+ // resolved, which means at least part of the tree was likely unblocked. Try
1680
+ // rendering again, at a new expiration time.
1675
1681
1676
- if ( ( boundaryFiber . mode & ConcurrentMode ) !== NoContext ) {
1677
- if ( root === nextRoot && nextRenderExpirationTime === suspendedTime ) {
1678
- // Received a ping at the same priority level at which we're currently
1679
- // rendering. Restart from the root.
1680
- nextRoot = null ;
1681
- }
1682
+ const retryCache : WeakSet < Thenable > | Set < Thenable > | null =
1683
+ boundaryFiber.stateNode;
1684
+ if (retryCache !== null) {
1685
+ // The thenable resolved, so we no longer need to memoize, because it will
1686
+ // never be thrown again.
1687
+ retryCache . delete ( thenable ) ;
1682
1688
}
1683
1689
1684
- scheduleWorkToRoot ( boundaryFiber , retryTime ) ;
1685
- if ( ( boundaryFiber . mode & ConcurrentMode ) === NoContext ) {
1686
- // Outside of concurrent mode, we must schedule an update on the source
1687
- // fiber, too, since it already committed in an inconsistent state and
1688
- // therefore does not have any pending work.
1689
- scheduleWorkToRoot ( sourceFiber , retryTime ) ;
1690
- const sourceTag = sourceFiber . tag ;
1691
- if ( sourceTag === ClassComponent && sourceFiber . stateNode !== null ) {
1692
- // When we try rendering again, we should not reuse the current fiber,
1693
- // since it's known to be in an inconsistent state. Use a force updte to
1694
- // prevent a bail out.
1695
- const update = createUpdate ( retryTime ) ;
1696
- update . tag = ForceUpdate ;
1697
- enqueueUpdate ( sourceFiber , update ) ;
1690
+ const currentTime = requestCurrentTime();
1691
+ const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);
1692
+ const root = scheduleWorkToRoot(boundaryFiber, retryTime);
1693
+ if (root !== null) {
1694
+ markPendingPriorityLevel ( root , retryTime ) ;
1695
+ const rootExpirationTime = root . expirationTime ;
1696
+ if ( rootExpirationTime !== NoWork ) {
1697
+ requestWork ( root , rootExpirationTime ) ;
1698
1698
}
1699
1699
}
1700
-
1701
- const rootExpirationTime = root . expirationTime ;
1702
- if ( rootExpirationTime !== NoWork ) {
1703
- requestWork ( root , rootExpirationTime ) ;
1704
- }
1705
1700
}
1706
1701
1707
1702
function scheduleWorkToRoot ( fiber : Fiber , expirationTime ) : FiberRoot | null {
@@ -2550,7 +2545,8 @@ export {
2550
2545
onUncaughtError ,
2551
2546
renderDidSuspend ,
2552
2547
renderDidError ,
2553
- retrySuspendedRoot,
2548
+ pingSuspendedRoot ,
2549
+ retryTimedOutBoundary ,
2554
2550
markLegacyErrorBoundaryAsFailed ,
2555
2551
isAlreadyFailedLegacyErrorBoundary ,
2556
2552
scheduleWork ,
0 commit comments