@@ -27,7 +27,6 @@ import type {
27
27
SuspenseState ,
28
28
SuspenseListRenderState ,
29
29
} from './ReactFiberSuspenseComponent.old' ;
30
- import type { SuspenseContext } from './ReactFiberSuspenseContext.old' ;
31
30
import type { OffscreenState } from './ReactFiberOffscreenComponent' ;
32
31
import type { TracingMarkerInstance } from './ReactFiberTracingMarkerComponent.old' ;
33
32
import type { Cache } from './ReactFiberCacheComponent.old' ;
@@ -110,14 +109,17 @@ import {
110
109
} from './ReactFiberHostContext.old' ;
111
110
import {
112
111
suspenseStackCursor ,
113
- InvisibleParentSuspenseContext ,
114
- hasSuspenseContext ,
115
- popSuspenseContext ,
116
- pushSuspenseContext ,
117
- setShallowSuspenseContext ,
112
+ popSuspenseListContext ,
113
+ popSuspenseHandler ,
114
+ pushSuspenseListContext ,
115
+ setShallowSuspenseListContext ,
118
116
ForceSuspenseFallback ,
119
- setDefaultShallowSuspenseContext ,
117
+ setDefaultShallowSuspenseListContext ,
120
118
} from './ReactFiberSuspenseContext.old' ;
119
+ import {
120
+ popHiddenContext ,
121
+ isCurrentTreeHidden ,
122
+ } from './ReactFiberHiddenContext.old' ;
121
123
import { findFirstSuspended } from './ReactFiberSuspenseComponent.old' ;
122
124
import {
123
125
isContextProvider as isLegacyContextProvider ,
@@ -147,9 +149,7 @@ import {
147
149
renderDidSuspend ,
148
150
renderDidSuspendDelayIfPossible ,
149
151
renderHasNotSuspendedYet ,
150
- popRenderLanes ,
151
152
getRenderTargetTime ,
152
- subtreeRenderLanes ,
153
153
getWorkInProgressTransitions ,
154
154
} from './ReactFiberWorkLoop.old' ;
155
155
import {
@@ -1086,7 +1086,7 @@ function completeWork(
1086
1086
return null ;
1087
1087
}
1088
1088
case SuspenseComponent : {
1089
- popSuspenseContext ( workInProgress ) ;
1089
+ popSuspenseHandler ( workInProgress ) ;
1090
1090
const nextState : null | SuspenseState = workInProgress . memoizedState ;
1091
1091
1092
1092
// Special path for dehydrated boundaries. We may eventually move this
@@ -1195,25 +1195,23 @@ function completeWork(
1195
1195
// If this render already had a ping or lower pri updates,
1196
1196
// and this is the first time we know we're going to suspend we
1197
1197
// should be able to immediately restart from within throwException.
1198
- const hasInvisibleChildContext =
1199
- current === null &&
1200
- ( workInProgress . memoizedProps . unstable_avoidThisFallback !==
1201
- true ||
1202
- ! enableSuspenseAvoidThisFallback ) ;
1203
- if (
1204
- hasInvisibleChildContext ||
1205
- hasSuspenseContext (
1206
- suspenseStackCursor . current ,
1207
- ( InvisibleParentSuspenseContext : SuspenseContext ) ,
1208
- )
1209
- ) {
1210
- // If this was in an invisible tree or a new render, then showing
1211
- // this boundary is ok.
1212
- renderDidSuspend ( ) ;
1213
- } else {
1214
- // Otherwise, we're going to have to hide content so we should
1215
- // suspend for longer if possible.
1198
+
1199
+ // Check if this is a "bad" fallback state or a good one. A bad
1200
+ // fallback state is one that we only show as a last resort; if this
1201
+ // is a transition, we'll block it from displaying, and wait for
1202
+ // more data to arrive.
1203
+ const isBadFallback =
1204
+ // It's bad to switch to a fallback if content is already visible
1205
+ ( current !== null && ! prevDidTimeout && ! isCurrentTreeHidden ( ) ) ||
1206
+ // Experimental: Some fallbacks are always bad
1207
+ ( enableSuspenseAvoidThisFallback &&
1208
+ workInProgress . memoizedProps . unstable_avoidThisFallback ===
1209
+ true ) ;
1210
+
1211
+ if ( isBadFallback ) {
1216
1212
renderDidSuspendDelayIfPossible ( ) ;
1213
+ } else {
1214
+ renderDidSuspend ( ) ;
1217
1215
}
1218
1216
}
1219
1217
}
@@ -1275,7 +1273,7 @@ function completeWork(
1275
1273
return null ;
1276
1274
}
1277
1275
case SuspenseListComponent: {
1278
- popSuspenseContext ( workInProgress ) ;
1276
+ popSuspenseListContext ( workInProgress ) ;
1279
1277
1280
1278
const renderState : null | SuspenseListRenderState =
1281
1279
workInProgress . memoizedState ;
@@ -1341,11 +1339,11 @@ function completeWork(
1341
1339
workInProgress . subtreeFlags = NoFlags ;
1342
1340
resetChildFibers ( workInProgress , renderLanes ) ;
1343
1341
1344
- // Set up the Suspense Context to force suspense and immediately
1345
- // rerender the children.
1346
- pushSuspenseContext (
1342
+ // Set up the Suspense List Context to force suspense and
1343
+ // immediately rerender the children.
1344
+ pushSuspenseListContext (
1347
1345
workInProgress ,
1348
- setShallowSuspenseContext (
1346
+ setShallowSuspenseListContext (
1349
1347
suspenseStackCursor . current ,
1350
1348
ForceSuspenseFallback ,
1351
1349
) ,
@@ -1468,14 +1466,16 @@ function completeWork(
1468
1466
// setting it the first time we go from not suspended to suspended.
1469
1467
let suspenseContext = suspenseStackCursor . current ;
1470
1468
if ( didSuspendAlready ) {
1471
- suspenseContext = setShallowSuspenseContext (
1469
+ suspenseContext = setShallowSuspenseListContext (
1472
1470
suspenseContext ,
1473
1471
ForceSuspenseFallback ,
1474
1472
) ;
1475
1473
} else {
1476
- suspenseContext = setDefaultShallowSuspenseContext ( suspenseContext ) ;
1474
+ suspenseContext = setDefaultShallowSuspenseListContext (
1475
+ suspenseContext ,
1476
+ ) ;
1477
1477
}
1478
- pushSuspenseContext ( workInProgress , suspenseContext ) ;
1478
+ pushSuspenseListContext ( workInProgress , suspenseContext ) ;
1479
1479
// Do a pass over the next row.
1480
1480
// Don't bubble properties in this case.
1481
1481
return next ;
@@ -1508,7 +1508,8 @@ function completeWork(
1508
1508
}
1509
1509
case OffscreenComponent:
1510
1510
case LegacyHiddenComponent: {
1511
- popRenderLanes ( workInProgress ) ;
1511
+ popSuspenseHandler ( workInProgress ) ;
1512
+ popHiddenContext ( workInProgress ) ;
1512
1513
const nextState : OffscreenState | null = workInProgress . memoizedState ;
1513
1514
const nextIsHidden = nextState !== null ;
1514
1515
@@ -1529,7 +1530,11 @@ function completeWork(
1529
1530
} else {
1530
1531
// Don't bubble properties for hidden children unless we're rendering
1531
1532
// at offscreen priority.
1532
- if ( includesSomeLane ( subtreeRenderLanes , ( OffscreenLane : Lane ) ) ) {
1533
+ if (
1534
+ includesSomeLane ( renderLanes , ( OffscreenLane : Lane ) ) &&
1535
+ // Also don't bubble if the tree suspended
1536
+ ( workInProgress . flags & DidCapture ) === NoLanes
1537
+ ) {
1533
1538
bubbleProperties ( workInProgress ) ;
1534
1539
// Check if there was an insertion or update in the hidden subtree.
1535
1540
// If so, we need to hide those nodes in the commit phase, so
@@ -1544,6 +1549,12 @@ function completeWork(
1544
1549
}
1545
1550
}
1546
1551
1552
+ if ( workInProgress . updateQueue !== null ) {
1553
+ // Schedule an effect to attach Suspense retry listeners
1554
+ // TODO: Move to passive phase
1555
+ workInProgress . flags |= Update ;
1556
+ }
1557
+
1547
1558
if ( enableCache ) {
1548
1559
let previousCache : Cache | null = null ;
1549
1560
if (
0 commit comments