@@ -1570,37 +1570,88 @@ function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) {
15701570 }
15711571}
15721572
1573- const SUSPENDED_MARKER : SuspenseState = {
1574- dehydrated : null ,
1575- retryTime : NoWork ,
1576- } ;
1573+ function mountSuspenseState (
1574+ renderExpirationTime : ExpirationTime ,
1575+ ) : SuspenseState {
1576+ return {
1577+ dehydrated : null ,
1578+ skippedTime : renderExpirationTime ,
1579+ retryTime : NoWork ,
1580+ } ;
1581+ }
1582+
1583+ function updateSuspenseState (
1584+ prevSuspenseState : SuspenseState ,
1585+ renderExpirationTime : ExpirationTime ,
1586+ ) : SuspenseState {
1587+ const prevSuspendedTime = prevSuspenseState . skippedTime ;
1588+ return {
1589+ dehydrated : null ,
1590+ skippedTime :
1591+ // Choose whichever time is a superset of the other one
1592+ prevSuspendedTime !== NoWork && prevSuspendedTime < renderExpirationTime
1593+ ? prevSuspendedTime
1594+ : renderExpirationTime ,
1595+ retryTime : NoWork ,
1596+ } ;
1597+ }
15771598
15781599function shouldRemainOnFallback (
15791600 suspenseContext : SuspenseContext ,
15801601 current : null | Fiber ,
15811602 workInProgress : Fiber ,
1603+ renderExpirationTime : ExpirationTime ,
15821604) {
1583- // If the context is telling us that we should show a fallback, and we're not
1584- // already showing content, then we should show the fallback instead.
1585- return (
1586- hasSuspenseContext (
1605+ if ( current === null ) {
1606+ return hasSuspenseContext (
15871607 suspenseContext ,
15881608 ( ForceSuspenseFallback : SuspenseContext ) ,
1589- ) &&
1590- ( current === null || current . memoizedState !== null )
1609+ ) ;
1610+ }
1611+
1612+ const suspenseState : SuspenseState = current . memoizedState ;
1613+ if ( suspenseState === null ) {
1614+ // Not already suspended.
1615+ return false ;
1616+ }
1617+
1618+ const skippedTime = suspenseState . skippedTime ;
1619+ if ( skippedTime !== NoWork && skippedTime < renderExpirationTime ) {
1620+ return true ;
1621+ }
1622+
1623+ return hasSuspenseContext (
1624+ suspenseContext ,
1625+ ( ForceSuspenseFallback : SuspenseContext ) ,
15911626 ) ;
15921627}
15931628
15941629function getRemainingWorkInPrimaryTree (
1595- workInProgress ,
1596- currentChildExpirationTime ,
1630+ current : Fiber ,
1631+ workInProgress : Fiber ,
1632+ currentPrimaryChildFragment : Fiber | null ,
15971633 renderExpirationTime ,
15981634) {
1635+ const currentSuspenseState : SuspenseState = current . memoizedState ;
1636+ if ( currentSuspenseState !== null ) {
1637+ const skippedTime = currentSuspenseState . skippedTime ;
1638+ if ( skippedTime !== NoWork && skippedTime < renderExpirationTime ) {
1639+ return skippedTime ;
1640+ }
1641+ }
1642+
1643+ const currentParentOfPrimaryChildren =
1644+ currentPrimaryChildFragment !== null
1645+ ? currentPrimaryChildFragment
1646+ : current ;
1647+ const currentChildExpirationTime =
1648+ currentParentOfPrimaryChildren . childExpirationTime ;
15991649 if ( currentChildExpirationTime < renderExpirationTime ) {
16001650 // The highest priority remaining work is not part of this render. So the
16011651 // remaining work has not changed.
16021652 return currentChildExpirationTime ;
16031653 }
1654+
16041655 if ( ( workInProgress . mode & BlockingMode ) !== NoMode ) {
16051656 // The highest priority remaining work is part of this render. Since we only
16061657 // keep track of the highest level, we don't know if there's a lower
@@ -1642,7 +1693,12 @@ function updateSuspenseComponent(
16421693
16431694 if (
16441695 didSuspend ||
1645- shouldRemainOnFallback ( suspenseContext , current , workInProgress )
1696+ shouldRemainOnFallback (
1697+ suspenseContext ,
1698+ current ,
1699+ workInProgress ,
1700+ renderExpirationTime ,
1701+ )
16461702 ) {
16471703 // Something in this boundary's subtree already suspended. Switch to
16481704 // rendering the fallback children.
@@ -1758,7 +1814,7 @@ function updateSuspenseComponent(
17581814 primaryChildFragment . sibling = fallbackChildFragment ;
17591815 // Skip the primary children, and continue working on the
17601816 // fallback children.
1761- workInProgress . memoizedState = SUSPENDED_MARKER ;
1817+ workInProgress . memoizedState = mountSuspenseState ( renderExpirationTime ) ;
17621818 workInProgress . child = primaryChildFragment ;
17631819 return fallbackChildFragment ;
17641820 } else {
@@ -1862,15 +1918,15 @@ function updateSuspenseComponent(
18621918 primaryChildFragment . sibling = fallbackChildFragment ;
18631919 fallbackChildFragment . effectTag |= Placement ;
18641920 primaryChildFragment . childExpirationTime = getRemainingWorkInPrimaryTree (
1921+ current ,
18651922 workInProgress ,
1866- // This argument represents the remaining work in the current
1867- // primary tree. Since the current tree did not already time out
1868- // the direct parent of the primary children is the Suspense
1869- // fiber, not a fragment.
1870- current . childExpirationTime ,
1923+ null ,
1924+ renderExpirationTime ,
1925+ ) ;
1926+ workInProgress . memoizedState = updateSuspenseState (
1927+ current . memoizedState ,
18711928 renderExpirationTime ,
18721929 ) ;
1873- workInProgress . memoizedState = SUSPENDED_MARKER ;
18741930 workInProgress . child = primaryChildFragment ;
18751931
18761932 // Skip the primary children, and continue working on the
@@ -1933,13 +1989,17 @@ function updateSuspenseComponent(
19331989 fallbackChildFragment . return = workInProgress ;
19341990 primaryChildFragment . sibling = fallbackChildFragment ;
19351991 primaryChildFragment . childExpirationTime = getRemainingWorkInPrimaryTree (
1992+ current ,
19361993 workInProgress ,
1937- currentPrimaryChildFragment . childExpirationTime ,
1994+ currentPrimaryChildFragment ,
19381995 renderExpirationTime ,
19391996 ) ;
19401997 // Skip the primary children, and continue working on the
19411998 // fallback children.
1942- workInProgress . memoizedState = SUSPENDED_MARKER ;
1999+ workInProgress . memoizedState = updateSuspenseState (
2000+ current . memoizedState ,
2001+ renderExpirationTime ,
2002+ ) ;
19432003 workInProgress . child = primaryChildFragment ;
19442004 return fallbackChildFragment ;
19452005 } else {
@@ -2031,17 +2091,14 @@ function updateSuspenseComponent(
20312091 primaryChildFragment . sibling = fallbackChildFragment ;
20322092 fallbackChildFragment . effectTag |= Placement ;
20332093 primaryChildFragment . childExpirationTime = getRemainingWorkInPrimaryTree (
2094+ current ,
20342095 workInProgress ,
2035- // This argument represents the remaining work in the current
2036- // primary tree. Since the current tree did not already time out
2037- // the direct parent of the primary children is the Suspense
2038- // fiber, not a fragment.
2039- current . childExpirationTime ,
2096+ null ,
20402097 renderExpirationTime ,
20412098 ) ;
20422099 // Skip the primary children, and continue working on the
20432100 // fallback children.
2044- workInProgress . memoizedState = SUSPENDED_MARKER ;
2101+ workInProgress . memoizedState = mountSuspenseState ( renderExpirationTime ) ;
20452102 workInProgress . child = primaryChildFragment ;
20462103 return fallbackChildFragment ;
20472104 } else {
0 commit comments