Skip to content

Commit 1b09ec0

Browse files
committed
Re-add old Fabric Offscreen impl behind flag
There's a chance that facebook#21960 will affect layout in a way that we don't expect, so I'm adding back the old implementation so we can toggle the feature with a flag. The flag should read from the ReactNativeFeatureFlags shim so that we can change it at runtime. I'll do that separately.
1 parent 8a37b0e commit 1b09ec0

20 files changed

+259
-34
lines changed

packages/react-native-renderer/src/ReactFabricHostConfig.js

+26
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,32 @@ export function getOffscreenContainerProps(
457457
}
458458
}
459459

460+
export function cloneHiddenInstance(
461+
instance: Instance,
462+
type: string,
463+
props: Props,
464+
internalInstanceHandle: Object,
465+
): Instance {
466+
const viewConfig = instance.canonical.viewConfig;
467+
const node = instance.node;
468+
const updatePayload = create(
469+
{style: {display: 'none'}},
470+
viewConfig.validAttributes,
471+
);
472+
return {
473+
node: cloneNodeWithNewProps(node, updatePayload),
474+
canonical: instance.canonical,
475+
};
476+
}
477+
478+
export function cloneHiddenTextInstance(
479+
instance: Instance,
480+
text: string,
481+
internalInstanceHandle: Object,
482+
): TextInstance {
483+
throw new Error('Not yet implemented.');
484+
}
485+
460486
export function createContainerChildSet(container: Container): ChildSet {
461487
return createChildNodeSet(container);
462488
}

packages/react-noop-renderer/src/createReactNoop.js

+48
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,54 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
582582
children,
583583
};
584584
},
585+
586+
cloneHiddenInstance(
587+
instance: Instance,
588+
type: string,
589+
props: Props,
590+
internalInstanceHandle: Object,
591+
): Instance {
592+
const clone = cloneInstance(
593+
instance,
594+
null,
595+
type,
596+
props,
597+
props,
598+
internalInstanceHandle,
599+
true,
600+
null,
601+
);
602+
clone.hidden = true;
603+
return clone;
604+
},
605+
606+
cloneHiddenTextInstance(
607+
instance: TextInstance,
608+
text: string,
609+
internalInstanceHandle: Object,
610+
): TextInstance {
611+
const clone = {
612+
text: instance.text,
613+
id: instance.id,
614+
parent: instance.parent,
615+
hidden: true,
616+
context: instance.context,
617+
};
618+
// Hide from unit tests
619+
Object.defineProperty(clone, 'id', {
620+
value: clone.id,
621+
enumerable: false,
622+
});
623+
Object.defineProperty(clone, 'parent', {
624+
value: clone.parent,
625+
enumerable: false,
626+
});
627+
Object.defineProperty(clone, 'context', {
628+
value: clone.context,
629+
enumerable: false,
630+
});
631+
return clone;
632+
},
585633
};
586634

587635
const NoopRenderer = reconciler(hostConfig);

packages/react-reconciler/src/ReactFiberBeginWork.new.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ import {
8989
enableLazyContextPropagation,
9090
enableSuspenseLayoutEffectSemantics,
9191
enableSchedulingProfiler,
92+
enablePersistentOffscreenHostContainer,
9293
} from 'shared/ReactFeatureFlags';
9394
import invariant from 'shared/invariant';
9495
import isArray from 'shared/isArray';
@@ -744,7 +745,7 @@ function updateOffscreenComponent(
744745
workInProgress.updateQueue = spawnedCachePool;
745746
}
746747

747-
if (supportsPersistence) {
748+
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
748749
// In persistent mode, the offscreen children are wrapped in a host node.
749750
// TODO: Optimize this to use the OffscreenComponent fiber instead of
750751
// an extra HostComponent fiber. Need to make sure this doesn't break Fabric
@@ -760,12 +761,10 @@ function updateOffscreenComponent(
760761
renderLanes,
761762
);
762763
return offscreenContainer;
763-
}
764-
if (supportsMutation) {
764+
} else {
765765
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
766766
return workInProgress.child;
767767
}
768-
return null;
769768
}
770769

771770
function reconcileOffscreenHostContainer(
@@ -2383,7 +2382,7 @@ function updateSuspenseFallbackChildren(
23832382
currentPrimaryChildFragment.treeBaseDuration;
23842383
}
23852384

2386-
if (supportsPersistence) {
2385+
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
23872386
// In persistent mode, the offscreen children are wrapped in a host node.
23882387
// We need to complete it now, because we're going to skip over its normal
23892388
// complete phase and go straight to rendering the fallback.
@@ -2411,7 +2410,7 @@ function updateSuspenseFallbackChildren(
24112410
primaryChildProps,
24122411
);
24132412

2414-
if (supportsPersistence) {
2413+
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
24152414
// In persistent mode, the offscreen children are wrapped in a host node.
24162415
// We need to complete it now, because we're going to skip over its normal
24172416
// complete phase and go straight to rendering the fallback.

packages/react-reconciler/src/ReactFiberBeginWork.old.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ import {
8989
enableLazyContextPropagation,
9090
enableSuspenseLayoutEffectSemantics,
9191
enableSchedulingProfiler,
92+
enablePersistentOffscreenHostContainer,
9293
} from 'shared/ReactFeatureFlags';
9394
import invariant from 'shared/invariant';
9495
import isArray from 'shared/isArray';
@@ -744,7 +745,7 @@ function updateOffscreenComponent(
744745
workInProgress.updateQueue = spawnedCachePool;
745746
}
746747

747-
if (supportsPersistence) {
748+
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
748749
// In persistent mode, the offscreen children are wrapped in a host node.
749750
// TODO: Optimize this to use the OffscreenComponent fiber instead of
750751
// an extra HostComponent fiber. Need to make sure this doesn't break Fabric
@@ -760,12 +761,10 @@ function updateOffscreenComponent(
760761
renderLanes,
761762
);
762763
return offscreenContainer;
763-
}
764-
if (supportsMutation) {
764+
} else {
765765
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
766766
return workInProgress.child;
767767
}
768-
return null;
769768
}
770769

771770
function reconcileOffscreenHostContainer(
@@ -2383,7 +2382,7 @@ function updateSuspenseFallbackChildren(
23832382
currentPrimaryChildFragment.treeBaseDuration;
23842383
}
23852384

2386-
if (supportsPersistence) {
2385+
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
23872386
// In persistent mode, the offscreen children are wrapped in a host node.
23882387
// We need to complete it now, because we're going to skip over its normal
23892388
// complete phase and go straight to rendering the fallback.
@@ -2411,7 +2410,7 @@ function updateSuspenseFallbackChildren(
24112410
primaryChildProps,
24122411
);
24132412

2414-
if (supportsPersistence) {
2413+
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
24152414
// In persistent mode, the offscreen children are wrapped in a host node.
24162415
// We need to complete it now, because we're going to skip over its normal
24172416
// complete phase and go straight to rendering the fallback.

packages/react-reconciler/src/ReactFiberCompleteWork.new.js

+77-10
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ import {
8484
supportsMutation,
8585
supportsPersistence,
8686
cloneInstance,
87+
cloneHiddenInstance,
88+
cloneHiddenTextInstance,
8789
createContainerChildSet,
8890
appendChildToContainerChildSet,
8991
finalizeContainerChildren,
@@ -128,6 +130,7 @@ import {
128130
enableProfilerTimer,
129131
enableCache,
130132
enableSuspenseLayoutEffectSemantics,
133+
enablePersistentOffscreenHostContainer,
131134
} from 'shared/ReactFeatureFlags';
132135
import {
133136
renderDidSuspend,
@@ -198,7 +201,12 @@ let updateHostText;
198201
if (supportsMutation) {
199202
// Mutation mode
200203

201-
appendAllChildren = function(parent: Instance, workInProgress: Fiber) {
204+
appendAllChildren = function(
205+
parent: Instance,
206+
workInProgress: Fiber,
207+
needsVisibilityToggle: boolean,
208+
isHidden: boolean,
209+
) {
202210
// We only have the top Fiber that was created but we need recurse down its
203211
// children to find all the terminal nodes.
204212
let node = workInProgress.child;
@@ -286,22 +294,53 @@ if (supportsMutation) {
286294
} else if (supportsPersistence) {
287295
// Persistent host tree mode
288296

289-
appendAllChildren = function(parent: Instance, workInProgress: Fiber) {
297+
appendAllChildren = function(
298+
parent: Instance,
299+
workInProgress: Fiber,
300+
needsVisibilityToggle: boolean,
301+
isHidden: boolean,
302+
) {
290303
// We only have the top Fiber that was created but we need recurse down its
291304
// children to find all the terminal nodes.
292305
let node = workInProgress.child;
293306
while (node !== null) {
294307
// eslint-disable-next-line no-labels
295308
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+
}
297316
appendInitialChild(parent, instance);
298317
} 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+
}
300324
appendInitialChild(parent, instance);
301325
} else if (node.tag === HostPortal) {
302326
// If we have a portal child, then we don't want to traverse
303327
// down its children. Instead, we'll get insertions from each child in
304328
// 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+
}
305344
} else if (node.child !== null) {
306345
node.child.return = node;
307346
node = node.child;
@@ -327,22 +366,50 @@ if (supportsMutation) {
327366
const appendAllChildrenToContainer = function(
328367
containerChildSet: ChildSet,
329368
workInProgress: Fiber,
369+
needsVisibilityToggle: boolean,
370+
isHidden: boolean,
330371
) {
331372
// We only have the top Fiber that was created but we need recurse down its
332373
// children to find all the terminal nodes.
333374
let node = workInProgress.child;
334375
while (node !== null) {
335376
// eslint-disable-next-line no-labels
336377
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+
}
338385
appendChildToContainerChildSet(containerChildSet, instance);
339386
} 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+
}
341393
appendChildToContainerChildSet(containerChildSet, instance);
342394
} else if (node.tag === HostPortal) {
343395
// If we have a portal child, then we don't want to traverse
344396
// down its children. Instead, we'll get insertions from each child in
345397
// 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+
}
346413
} else if (node.child !== null) {
347414
node.child.return = node;
348415
node = node.child;
@@ -376,7 +443,7 @@ if (supportsMutation) {
376443
const container = portalOrRoot.containerInfo;
377444
const newChildSet = createContainerChildSet(container);
378445
// If children might have changed, we have to add them all to the set.
379-
appendAllChildrenToContainer(newChildSet, workInProgress);
446+
appendAllChildrenToContainer(newChildSet, workInProgress, false, false);
380447
portalOrRoot.pendingChildren = newChildSet;
381448
// Schedule an update on the container to swap out the container.
382449
markUpdate(workInProgress);
@@ -449,7 +516,7 @@ if (supportsMutation) {
449516
markUpdate(workInProgress);
450517
} else {
451518
// If children might have changed, we have to add them all to the set.
452-
appendAllChildren(newInstance, workInProgress);
519+
appendAllChildren(newInstance, workInProgress, false, false);
453520
}
454521
};
455522
updateHostText = function(
@@ -722,7 +789,7 @@ export function completeSuspendedOffscreenHostContainer(
722789
workInProgress,
723790
);
724791

725-
appendAllChildren(instance, workInProgress);
792+
appendAllChildren(instance, workInProgress, false, false);
726793

727794
workInProgress.stateNode = instance;
728795

@@ -869,7 +936,7 @@ function completeWork(
869936
workInProgress,
870937
);
871938

872-
appendAllChildren(instance, workInProgress);
939+
appendAllChildren(instance, workInProgress, false, false);
873940

874941
workInProgress.stateNode = instance;
875942

0 commit comments

Comments
 (0)