324
// If we have a portal child, then we don't want to traverse
325
// down its children. Instead, we'll get insertions from each child in
326
// the portal directly.
327
- } else if (node.tag === SuspenseComponent) {
328
- if ((node.flags & Visibility) !== NoFlags) {
329
- // Need to toggle the visibility of the primary children.
330
- const newIsHidden = node.memoizedState !== null;
331
- if (newIsHidden) {
332
- const primaryChildParent = node.child;
333
- if (primaryChildParent !== null) {
334
- if (primaryChildParent.child !== null) {
335
- primaryChildParent.child.return = primaryChildParent;
336
- appendAllChildren(
337
- parent,
338
- primaryChildParent,
339
- true,
340
- newIsHidden,
341
- );
342
- }
343
- const fallbackChildParent = primaryChildParent.sibling;
344
- if (fallbackChildParent !== null) {
345
- fallbackChildParent.return = node;
346
- node = fallbackChildParent;
347
- continue;
348
- }
349
- }
350
- }
351
- }
352
- if (node.child !== null) {
353
- // Continue traversing like normal
354
- node.child.return = node;
355
- node = node.child;
356
- continue;
327
+ } else if (
328
+ node.tag === OffscreenComponent &&
329
+ node.memoizedState !== null
330
+ ) {
331
+ // The children in this boundary are hidden. Toggle their visibility
332
+ // before appending.
333
+ const child = node.child;
334
+ if (child !== null) {
335
+ child.return = node;
336
}
337
+ appendAllChildren(parent, node, true, true);
338
} else if (node.child !== null) {
339
node.child.return = node;
340
node = node.child;
389
// If we have a portal child, then we don't want to traverse
390
// down its children. Instead, we'll get insertions from each child in
391
// the portal directly.
412
- } else if (node.tag === SuspenseComponent) {
413
- if ((node.flags & Visibility) !== NoFlags) {
414
- // Need to toggle the visibility of the primary children.
415
- const newIsHidden = node.memoizedState !== null;
416
- if (newIsHidden) {
417
- const primaryChildParent = node.child;
418
- if (primaryChildParent !== null) {
419
- if (primaryChildParent.child !== null) {
420
- primaryChildParent.child.return = primaryChildParent;
421
- appendAllChildrenToContainer(
422
- containerChildSet,
423
- primaryChildParent,
424
- true,
425
- newIsHidden,
426
- );
427
- }
428
- const fallbackChildParent = primaryChildParent.sibling;
429
- if (fallbackChildParent !== null) {
430
- fallbackChildParent.return = node;
431
- node = fallbackChildParent;
432
- continue;
433
- }
434
- }
435
- }
436
- }
437
- if (node.child !== null) {
438
- // Continue traversing like normal
439
- node.child.return = node;
440
- node = node.child;
441
- continue;
392
+ } else if (
393
+ node.tag === OffscreenComponent &&
394
+ node.memoizedState !== null
395
+ ) {
396
+ // The children in this boundary are hidden. Toggle their visibility
397
+ // before appending.
398
+ const child = node.child;
399
+ if (child !== null) {
400
+ child.return = node;
401
}
402
+ appendAllChildrenToContainer(containerChildSet, node, true, true);
403
} else if (node.child !== null) {
404
node.child.return = node;
405
node = node.child;
1016
prevDidTimeout = prevState !== null;
1017
}
1018
1019
+ // If the suspended state of the boundary changes, we need to schedule
1020
+ // an effect to toggle the subtree's visibility. When we switch from
1021
+ // fallback -> primary, the inner Offscreen fiber schedules this effect
1022
+ // as part of its normal complete phase. But when we switch from
1023
+ // primary -> fallback, the inner Offscreen fiber does not have a complete
1024
+ // phase. So we need to schedule its effect here.
1025
+ //
1026
+ // We also use this flag to connect/disconnect the effects, but the same
1027
+ // logic applies: when re-connecting, the Offscreen fiber's complete
1028
+ // phase will handle scheduling the effect. It's only when the fallback
1029
+ // is active that we have to do anything special.
1030
if (nextDidTimeout && !prevDidTimeout) {
1031
+ const offscreenFiber: Fiber = (workInProgress.child: any);
1032
+ offscreenFiber.flags |= Visibility;
1033
+
1034
// TODO: This will still suspend a synchronous tree if anything
1035
// in the concurrent tree already suspended during this render.
1036
// This is a known bug.
1070
workInProgress.flags |= Update;
1071
}
1072
1099
- if (supportsMutation) {
1100
- if (nextDidTimeout !== prevDidTimeout) {
1101
- // In mutation mode, visibility is toggled by mutating the nearest
1102
- // host nodes whenever they switch from hidden -> visible or vice
1103
- // versa. We don't need to switch when the boundary updates but its
1104
- // visibility hasn't changed.
1105
- workInProgress.flags |= Visibility;
1106
- }
1107
- }
1108
- if (supportsPersistence) {
1109
- if (nextDidTimeout) {
1110
- // In persistent mode, visibility is toggled by cloning the nearest
1111
- // host nodes in the complete phase whenever the boundary is hidden.
1112
- // TODO: The plan is to add a transparent host wrapper (no layout)
1113
- // around the primary children and hide that node. Then we don't need
1114
- // to do the funky cloning business.
1115
- workInProgress.flags |= Visibility;
1116
- }
1117
- }
1118
-
1073
if (
1074
enableSuspenseCallback &&
1075
workInProgress.updateQueue !== null &&