181
enableViewTransition,
182
enableFizzBlockingRender,
183
enableAsyncDebugInfo,
184
+ enableCPUSuspense,
185
} from 'shared/ReactFeatureFlags';
186
187
import assign from 'shared/assign';
251
row: null | SuspenseListRow, // the row that this boundary blocks from completing.
252
completedSegments: Array<Segment>, // completed but not yet flushed segments.
253
byteSize: number, // used to determine whether to inline children boundaries.
254
+ defer: boolean, // never inline deferred boundaries
255
fallbackAbortableTasks: Set<Task>, // used to cancel task on the fallback if the boundary completes or gets canceled.
256
contentState: HoistableState,
257
fallbackState: HoistableState,
458
// The larger this limit is, the more we can save on preparing fallbacks in case we end up
459
// outlining.
460
return (
459
- (boundary.byteSize > 500 || hasSuspenseyContent(boundary.contentState)) &&
461
+ (boundary.byteSize > 500 ||
462
+ hasSuspenseyContent(boundary.contentState) ||
463
+ boundary.defer) &&
464
// For boundaries that can possibly contribute to the preamble we don't want to outline
465
// them regardless of their size since the fallbacks should only be emitted if we've
466
// errored the boundary.
786
fallbackAbortableTasks: Set<Task>,
787
contentPreamble: null | Preamble,
788
fallbackPreamble: null | Preamble,
789
+ defer: boolean,
790
): SuspenseBoundary {
791
const boundary: SuspenseBoundary = {
792
status: PENDING,
796
row: row,
797
completedSegments: [],
798
byteSize: 0,
799
+ defer: defer,
800
fallbackAbortableTasks,
801
errorDigest: null,
802
contentState: createHoistableState(),
1280
// in case it ends up generating a large subtree of content.
1281
const fallback: ReactNodeList = props.fallback;
1282
const content: ReactNodeList = props.children;
1283
+ const defer: boolean = enableCPUSuspense && props.defer === true;
1284
1285
const fallbackAbortSet: Set<Task> = new Set();
1286
let newBoundary: SuspenseBoundary;
1291
fallbackAbortSet,
1292
createPreambleState(),
1293
createPreambleState(),
1294
+ defer,
1295
);
1296
} else {
1297
newBoundary = createSuspenseBoundary(
1300
fallbackAbortSet,
1301
null,
1302
null,
1303
+ defer,
1304
);
1305
}
1306
if (request.trackedPostpones !== null) {
1336
// no parent segment so there's nothing to wait on.
1337
contentRootSegment.parentFlushed = true;
1338
1330
- if (request.trackedPostpones !== null) {
1339
+ const trackedPostpones = request.trackedPostpones;
1340
+ if (trackedPostpones !== null || defer) {
1341
+ // This is a prerender or deferred boundary. In this mode we want to render the fallback synchronously
1342
+ // and schedule the content to render later. This is the opposite of what we do during a normal render
1343
+ // where we try to skip rendering the fallback if the content itself can render synchronously
1344
+
1345
// Stash the original stack frame.
1346
const suspenseComponentStack = task.componentStack;
1333
- // This is a prerender. In this mode we want to render the fallback synchronously and schedule
1334
- // the content to render later. This is the opposite of what we do during a normal render
1335
- // where we try to skip rendering the fallback if the content itself can render synchronously
1336
- const trackedPostpones = request.trackedPostpones;
1347
1348
const fallbackKeyPath: KeyNode = [
1349
keyPath[0],
1350
'Suspense Fallback',
1351
keyPath[2],
1352
];
1343
- const fallbackReplayNode: ReplayNode = [
1344
- fallbackKeyPath[1],
1345
- fallbackKeyPath[2],
1346
- ([]: Array<ReplayNode>),
1347
- null,
1348
- ];
1349
- trackedPostpones.workingMap.set(fallbackKeyPath, fallbackReplayNode);
1350
- // We are rendering the fallback before the boundary content so we keep track of
1351
- // the fallback replay node until we determine if the primary content suspends
1352
- newBoundary.trackedFallbackNode = fallbackReplayNode;
1353
+ if (trackedPostpones !== null) {
1354
+ const fallbackReplayNode: ReplayNode = [
1355
+ fallbackKeyPath[1],
1356
+ fallbackKeyPath[2],
1357
+ ([]: Array<ReplayNode>),
1358
+ null,
1359
+ ];
1360
+ trackedPostpones.workingMap.set(fallbackKeyPath, fallbackReplayNode);
1361
+ // We are rendering the fallback before the boundary content so we keep track of
1362
+ // the fallback replay node until we determine if the primary content suspends
1363
+ newBoundary.trackedFallbackNode = fallbackReplayNode;
1364
+ }
1365
1366
task.blockedSegment = boundarySegment;
1367
task.blockedPreamble = newBoundary.fallbackPreamble;
1592
1593
const content: ReactNodeList = props.children;
1594
const fallback: ReactNodeList = props.fallback;
1595
+ const defer: boolean = enableCPUSuspense && props.defer === true;
1596
1597
const fallbackAbortSet: Set<Task> = new Set();
1598
let resumedBoundary: SuspenseBoundary;
1603
fallbackAbortSet,
1604
createPreambleState(),
1605
createPreambleState(),
1606
+ defer,
1607
);
1608
} else {
1609
resumedBoundary = createSuspenseBoundary(
1612
fallbackAbortSet,
1613
null,
1614
null,
1615
+ defer,
1616
);
1617
}
1618
resumedBoundary.parentFlushed = true;
4399
new Set(),
4400
null,
4401
null,
4402
+ false,
4403
);
4404
resumedBoundary.parentFlushed = true;
4405
// We restore the same id of this boundary as was used during prerender.
5509
!flushingPartialBoundaries &&
5510
isEligibleForOutlining(request, boundary) &&
5511
(flushedByteSize + boundary.byteSize > request.progressiveChunkSize ||
5496
- hasSuspenseyContent(boundary.contentState))
5512
+ hasSuspenseyContent(boundary.contentState) ||
5513
+ boundary.defer)
5514
) {
5515
// Inlining this boundary would make the current sequence being written too large
5516
// and block the parent for too long. Instead, it will be emitted separately so that we