@@ -84,6 +84,8 @@ import {
84
84
supportsMutation ,
85
85
supportsPersistence ,
86
86
cloneInstance ,
87
+ cloneHiddenInstance ,
88
+ cloneHiddenTextInstance ,
87
89
createContainerChildSet ,
88
90
appendChildToContainerChildSet ,
89
91
finalizeContainerChildren ,
@@ -128,6 +130,7 @@ import {
128
130
enableProfilerTimer ,
129
131
enableCache ,
130
132
enableSuspenseLayoutEffectSemantics ,
133
+ enablePersistentOffscreenHostContainer ,
131
134
} from 'shared/ReactFeatureFlags' ;
132
135
import {
133
136
renderDidSuspend ,
@@ -198,7 +201,12 @@ let updateHostText;
198
201
if ( supportsMutation ) {
199
202
// Mutation mode
200
203
201
- appendAllChildren = function ( parent : Instance , workInProgress : Fiber ) {
204
+ appendAllChildren = function (
205
+ parent : Instance ,
206
+ workInProgress : Fiber ,
207
+ needsVisibilityToggle : boolean ,
208
+ isHidden : boolean ,
209
+ ) {
202
210
// We only have the top Fiber that was created but we need recurse down its
203
211
// children to find all the terminal nodes.
204
212
let node = workInProgress . child ;
@@ -286,22 +294,53 @@ if (supportsMutation) {
286
294
} else if ( supportsPersistence ) {
287
295
// Persistent host tree mode
288
296
289
- appendAllChildren = function ( parent : Instance , workInProgress : Fiber ) {
297
+ appendAllChildren = function (
298
+ parent : Instance ,
299
+ workInProgress : Fiber ,
300
+ needsVisibilityToggle : boolean ,
301
+ isHidden : boolean ,
302
+ ) {
290
303
// We only have the top Fiber that was created but we need recurse down its
291
304
// children to find all the terminal nodes.
292
305
let node = workInProgress . child ;
293
306
while ( node !== null ) {
294
307
// eslint-disable-next-line no-labels
295
308
branches: if ( node . tag === HostComponent ) {
296
- const instance = node . stateNode ;
309
+ let instance = node . stateNode ;
310
+ if ( needsVisibilityToggle && isHidden ) {
311
+ // This child is inside a timed out tree. Hide it.
312
+ const props = node . memoizedProps ;
313
+ const type = node . type ;
314
+ instance = cloneHiddenInstance ( instance , type , props , node ) ;
315
+ }
297
316
appendInitialChild ( parent , instance ) ;
298
317
} else if ( node . tag === HostText ) {
299
- const instance = node . stateNode ;
318
+ let instance = node . stateNode ;
319
+ if ( needsVisibilityToggle && isHidden ) {
320
+ // This child is inside a timed out tree. Hide it.
321
+ const text = node . memoizedProps ;
322
+ instance = cloneHiddenTextInstance ( instance , text , node ) ;
323
+ }
300
324
appendInitialChild ( parent , instance ) ;
301
325
} else if ( node . tag === HostPortal ) {
302
326
// If we have a portal child, then we don't want to traverse
303
327
// down its children. Instead, we'll get insertions from each child in
304
328
// the portal directly.
329
+ } else if (
330
+ node . tag === OffscreenComponent &&
331
+ node . memoizedState !== null
332
+ ) {
333
+ // The children in this boundary are hidden. Toggle their visibility
334
+ // before appending.
335
+ const child = node . child ;
336
+ if ( child !== null ) {
337
+ child . return = node ;
338
+ }
339
+ if ( enablePersistentOffscreenHostContainer ) {
340
+ appendAllChildren ( parent , node , false , false ) ;
341
+ } else {
342
+ appendAllChildren ( parent , node , true , true ) ;
343
+ }
305
344
} else if ( node . child !== null ) {
306
345
node . child . return = node ;
307
346
node = node . child ;
@@ -327,22 +366,50 @@ if (supportsMutation) {
327
366
const appendAllChildrenToContainer = function (
328
367
containerChildSet : ChildSet ,
329
368
workInProgress : Fiber ,
369
+ needsVisibilityToggle : boolean ,
370
+ isHidden : boolean ,
330
371
) {
331
372
// We only have the top Fiber that was created but we need recurse down its
332
373
// children to find all the terminal nodes.
333
374
let node = workInProgress . child ;
334
375
while ( node !== null ) {
335
376
// eslint-disable-next-line no-labels
336
377
branches: if ( node . tag === HostComponent ) {
337
- const instance = node . stateNode ;
378
+ let instance = node . stateNode ;
379
+ if ( needsVisibilityToggle && isHidden ) {
380
+ // This child is inside a timed out tree. Hide it.
381
+ const props = node . memoizedProps ;
382
+ const type = node . type ;
383
+ instance = cloneHiddenInstance ( instance , type , props , node ) ;
384
+ }
338
385
appendChildToContainerChildSet ( containerChildSet , instance ) ;
339
386
} else if ( node . tag === HostText ) {
340
- const instance = node . stateNode ;
387
+ let instance = node . stateNode ;
388
+ if ( needsVisibilityToggle && isHidden ) {
389
+ // This child is inside a timed out tree. Hide it.
390
+ const text = node . memoizedProps ;
391
+ instance = cloneHiddenTextInstance ( instance , text , node ) ;
392
+ }
341
393
appendChildToContainerChildSet ( containerChildSet , instance ) ;
342
394
} else if ( node . tag === HostPortal ) {
343
395
// If we have a portal child, then we don't want to traverse
344
396
// down its children. Instead, we'll get insertions from each child in
345
397
// the portal directly.
398
+ } else if (
399
+ node . tag === OffscreenComponent &&
400
+ node . memoizedState !== null
401
+ ) {
402
+ // The children in this boundary are hidden. Toggle their visibility
403
+ // before appending.
404
+ const child = node . child ;
405
+ if ( child !== null ) {
406
+ child . return = node ;
407
+ }
408
+ if ( enablePersistentOffscreenHostContainer ) {
409
+ appendAllChildrenToContainer ( containerChildSet , node , false , false ) ;
410
+ } else {
411
+ appendAllChildrenToContainer ( containerChildSet , node , true , true ) ;
412
+ }
346
413
} else if ( node . child !== null ) {
347
414
node . child . return = node ;
348
415
node = node . child ;
@@ -376,7 +443,7 @@ if (supportsMutation) {
376
443
const container = portalOrRoot . containerInfo ;
377
444
const newChildSet = createContainerChildSet ( container ) ;
378
445
// If children might have changed, we have to add them all to the set.
379
- appendAllChildrenToContainer ( newChildSet , workInProgress ) ;
446
+ appendAllChildrenToContainer ( newChildSet , workInProgress , false , false ) ;
380
447
portalOrRoot . pendingChildren = newChildSet ;
381
448
// Schedule an update on the container to swap out the container.
382
449
markUpdate ( workInProgress ) ;
@@ -449,7 +516,7 @@ if (supportsMutation) {
449
516
markUpdate ( workInProgress ) ;
450
517
} else {
451
518
// If children might have changed, we have to add them all to the set.
452
- appendAllChildren ( newInstance , workInProgress ) ;
519
+ appendAllChildren ( newInstance , workInProgress , false , false ) ;
453
520
}
454
521
} ;
455
522
updateHostText = function (
@@ -722,7 +789,7 @@ export function completeSuspendedOffscreenHostContainer(
722
789
workInProgress ,
723
790
) ;
724
791
725
- appendAllChildren ( instance , workInProgress ) ;
792
+ appendAllChildren ( instance , workInProgress , false , false ) ;
726
793
727
794
workInProgress . stateNode = instance ;
728
795
@@ -869,7 +936,7 @@ function completeWork(
869
936
workInProgress ,
870
937
) ;
871
938
872
- appendAllChildren ( instance , workInProgress ) ;
939
+ appendAllChildren ( instance , workInProgress , false , false ) ;
873
940
874
941
workInProgress . stateNode = instance ;
875
942
0 commit comments