Skip to content

Commit

Permalink
Re-add old Fabric Offscreen impl behind flag (facebook#22018)
Browse files Browse the repository at this point in the history
* 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.

* Import dynamic RN flags from external module

Internal feature flags that we wish to control with a GK can now be
imported from an external module, which I've called
"ReactNativeInternalFeatureFlags".

We'll need to add this module to the downstream repo.

We can't yet use this in our tests, because we don't have a test
configuration that runs against the React Native feature flags fork. We
should set up that up the same way we did for www.
  • Loading branch information
acdlite authored Aug 4, 2021
1 parent 215db46 commit 19092ac
Show file tree
Hide file tree
Showing 25 changed files with 324 additions and 44 deletions.
26 changes: 26 additions & 0 deletions packages/react-native-renderer/src/ReactFabricHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,32 @@ export function getOffscreenContainerProps(
}
}

export function cloneHiddenInstance(
instance: Instance,
type: string,
props: Props,
internalInstanceHandle: Object,
): Instance {
const viewConfig = instance.canonical.viewConfig;
const node = instance.node;
const updatePayload = create(
{style: {display: 'none'}},
viewConfig.validAttributes,
);
return {
node: cloneNodeWithNewProps(node, updatePayload),
canonical: instance.canonical,
};
}

export function cloneHiddenTextInstance(
instance: Instance,
text: string,
internalInstanceHandle: Object,
): TextInstance {
throw new Error('Not yet implemented.');
}

export function createContainerChildSet(container: Container): ChildSet {
return createChildNodeSet(container);
}
Expand Down
48 changes: 48 additions & 0 deletions packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,54 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
children,
};
},

cloneHiddenInstance(
instance: Instance,
type: string,
props: Props,
internalInstanceHandle: Object,
): Instance {
const clone = cloneInstance(
instance,
null,
type,
props,
props,
internalInstanceHandle,
true,
null,
);
clone.hidden = true;
return clone;
},

cloneHiddenTextInstance(
instance: TextInstance,
text: string,
internalInstanceHandle: Object,
): TextInstance {
const clone = {
text: instance.text,
id: instance.id,
parent: instance.parent,
hidden: true,
context: instance.context,
};
// Hide from unit tests
Object.defineProperty(clone, 'id', {
value: clone.id,
enumerable: false,
});
Object.defineProperty(clone, 'parent', {
value: clone.parent,
enumerable: false,
});
Object.defineProperty(clone, 'context', {
value: clone.context,
enumerable: false,
});
return clone;
},
};

const NoopRenderer = reconciler(hostConfig);
Expand Down
12 changes: 5 additions & 7 deletions packages/react-reconciler/src/ReactFiberBeginWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import {
enableLazyContextPropagation,
enableSuspenseLayoutEffectSemantics,
enableSchedulingProfiler,
enablePersistentOffscreenHostContainer,
} from 'shared/ReactFeatureFlags';
import invariant from 'shared/invariant';
import isArray from 'shared/isArray';
Expand Down Expand Up @@ -146,7 +147,6 @@ import {
registerSuspenseInstanceRetry,
supportsHydration,
isPrimaryRenderer,
supportsMutation,
supportsPersistence,
getOffscreenContainerProps,
} from './ReactFiberHostConfig';
Expand Down Expand Up @@ -744,7 +744,7 @@ function updateOffscreenComponent(
workInProgress.updateQueue = spawnedCachePool;
}

if (supportsPersistence) {
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
// In persistent mode, the offscreen children are wrapped in a host node.
// TODO: Optimize this to use the OffscreenComponent fiber instead of
// an extra HostComponent fiber. Need to make sure this doesn't break Fabric
Expand All @@ -760,12 +760,10 @@ function updateOffscreenComponent(
renderLanes,
);
return offscreenContainer;
}
if (supportsMutation) {
} else {
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
return null;
}

function reconcileOffscreenHostContainer(
Expand Down Expand Up @@ -2383,7 +2381,7 @@ function updateSuspenseFallbackChildren(
currentPrimaryChildFragment.treeBaseDuration;
}

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

if (supportsPersistence) {
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
// In persistent mode, the offscreen children are wrapped in a host node.
// We need to complete it now, because we're going to skip over its normal
// complete phase and go straight to rendering the fallback.
Expand Down
12 changes: 5 additions & 7 deletions packages/react-reconciler/src/ReactFiberBeginWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import {
enableLazyContextPropagation,
enableSuspenseLayoutEffectSemantics,
enableSchedulingProfiler,
enablePersistentOffscreenHostContainer,
} from 'shared/ReactFeatureFlags';
import invariant from 'shared/invariant';
import isArray from 'shared/isArray';
Expand Down Expand Up @@ -146,7 +147,6 @@ import {
registerSuspenseInstanceRetry,
supportsHydration,
isPrimaryRenderer,
supportsMutation,
supportsPersistence,
getOffscreenContainerProps,
} from './ReactFiberHostConfig';
Expand Down Expand Up @@ -744,7 +744,7 @@ function updateOffscreenComponent(
workInProgress.updateQueue = spawnedCachePool;
}

if (supportsPersistence) {
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
// In persistent mode, the offscreen children are wrapped in a host node.
// TODO: Optimize this to use the OffscreenComponent fiber instead of
// an extra HostComponent fiber. Need to make sure this doesn't break Fabric
Expand All @@ -760,12 +760,10 @@ function updateOffscreenComponent(
renderLanes,
);
return offscreenContainer;
}
if (supportsMutation) {
} else {
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
return null;
}

function reconcileOffscreenHostContainer(
Expand Down Expand Up @@ -2383,7 +2381,7 @@ function updateSuspenseFallbackChildren(
currentPrimaryChildFragment.treeBaseDuration;
}

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

if (supportsPersistence) {
if (enablePersistentOffscreenHostContainer && supportsPersistence) {
// In persistent mode, the offscreen children are wrapped in a host node.
// We need to complete it now, because we're going to skip over its normal
// complete phase and go straight to rendering the fallback.
Expand Down
87 changes: 77 additions & 10 deletions packages/react-reconciler/src/ReactFiberCompleteWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ import {
supportsMutation,
supportsPersistence,
cloneInstance,
cloneHiddenInstance,
cloneHiddenTextInstance,
createContainerChildSet,
appendChildToContainerChildSet,
finalizeContainerChildren,
Expand Down Expand Up @@ -128,6 +130,7 @@ import {
enableProfilerTimer,
enableCache,
enableSuspenseLayoutEffectSemantics,
enablePersistentOffscreenHostContainer,
} from 'shared/ReactFeatureFlags';
import {
renderDidSuspend,
Expand Down Expand Up @@ -198,7 +201,12 @@ let updateHostText;
if (supportsMutation) {
// Mutation mode

appendAllChildren = function(parent: Instance, workInProgress: Fiber) {
appendAllChildren = function(
parent: Instance,
workInProgress: Fiber,
needsVisibilityToggle: boolean,
isHidden: boolean,
) {
// We only have the top Fiber that was created but we need recurse down its
// children to find all the terminal nodes.
let node = workInProgress.child;
Expand Down Expand Up @@ -286,22 +294,53 @@ if (supportsMutation) {
} else if (supportsPersistence) {
// Persistent host tree mode

appendAllChildren = function(parent: Instance, workInProgress: Fiber) {
appendAllChildren = function(
parent: Instance,
workInProgress: Fiber,
needsVisibilityToggle: boolean,
isHidden: boolean,
) {
// We only have the top Fiber that was created but we need recurse down its
// children to find all the terminal nodes.
let node = workInProgress.child;
while (node !== null) {
// eslint-disable-next-line no-labels
branches: if (node.tag === HostComponent) {
const instance = node.stateNode;
let instance = node.stateNode;
if (needsVisibilityToggle && isHidden) {
// This child is inside a timed out tree. Hide it.
const props = node.memoizedProps;
const type = node.type;
instance = cloneHiddenInstance(instance, type, props, node);
}
appendInitialChild(parent, instance);
} else if (node.tag === HostText) {
const instance = node.stateNode;
let instance = node.stateNode;
if (needsVisibilityToggle && isHidden) {
// This child is inside a timed out tree. Hide it.
const text = node.memoizedProps;
instance = cloneHiddenTextInstance(instance, text, node);
}
appendInitialChild(parent, instance);
} else if (node.tag === HostPortal) {
// If we have a portal child, then we don't want to traverse
// down its children. Instead, we'll get insertions from each child in
// the portal directly.
} else if (
node.tag === OffscreenComponent &&
node.memoizedState !== null
) {
// The children in this boundary are hidden. Toggle their visibility
// before appending.
const child = node.child;
if (child !== null) {
child.return = node;
}
if (enablePersistentOffscreenHostContainer) {
appendAllChildren(parent, node, false, false);
} else {
appendAllChildren(parent, node, true, true);
}
} else if (node.child !== null) {
node.child.return = node;
node = node.child;
Expand All @@ -327,22 +366,50 @@ if (supportsMutation) {
const appendAllChildrenToContainer = function(
containerChildSet: ChildSet,
workInProgress: Fiber,
needsVisibilityToggle: boolean,
isHidden: boolean,
) {
// We only have the top Fiber that was created but we need recurse down its
// children to find all the terminal nodes.
let node = workInProgress.child;
while (node !== null) {
// eslint-disable-next-line no-labels
branches: if (node.tag === HostComponent) {
const instance = node.stateNode;
let instance = node.stateNode;
if (needsVisibilityToggle && isHidden) {
// This child is inside a timed out tree. Hide it.
const props = node.memoizedProps;
const type = node.type;
instance = cloneHiddenInstance(instance, type, props, node);
}
appendChildToContainerChildSet(containerChildSet, instance);
} else if (node.tag === HostText) {
const instance = node.stateNode;
let instance = node.stateNode;
if (needsVisibilityToggle && isHidden) {
// This child is inside a timed out tree. Hide it.
const text = node.memoizedProps;
instance = cloneHiddenTextInstance(instance, text, node);
}
appendChildToContainerChildSet(containerChildSet, instance);
} else if (node.tag === HostPortal) {
// If we have a portal child, then we don't want to traverse
// down its children. Instead, we'll get insertions from each child in
// the portal directly.
} else if (
node.tag === OffscreenComponent &&
node.memoizedState !== null
) {
// The children in this boundary are hidden. Toggle their visibility
// before appending.
const child = node.child;
if (child !== null) {
child.return = node;
}
if (enablePersistentOffscreenHostContainer) {
appendAllChildrenToContainer(containerChildSet, node, false, false);
} else {
appendAllChildrenToContainer(containerChildSet, node, true, true);
}
} else if (node.child !== null) {
node.child.return = node;
node = node.child;
Expand Down Expand Up @@ -376,7 +443,7 @@ if (supportsMutation) {
const container = portalOrRoot.containerInfo;
const newChildSet = createContainerChildSet(container);
// If children might have changed, we have to add them all to the set.
appendAllChildrenToContainer(newChildSet, workInProgress);
appendAllChildrenToContainer(newChildSet, workInProgress, false, false);
portalOrRoot.pendingChildren = newChildSet;
// Schedule an update on the container to swap out the container.
markUpdate(workInProgress);
Expand Down Expand Up @@ -449,7 +516,7 @@ if (supportsMutation) {
markUpdate(workInProgress);
} else {
// If children might have changed, we have to add them all to the set.
appendAllChildren(newInstance, workInProgress);
appendAllChildren(newInstance, workInProgress, false, false);
}
};
updateHostText = function(
Expand Down Expand Up @@ -722,7 +789,7 @@ export function completeSuspendedOffscreenHostContainer(
workInProgress,
);

appendAllChildren(instance, workInProgress);
appendAllChildren(instance, workInProgress, false, false);

workInProgress.stateNode = instance;

Expand Down Expand Up @@ -869,7 +936,7 @@ function completeWork(
workInProgress,
);

appendAllChildren(instance, workInProgress);
appendAllChildren(instance, workInProgress, false, false);

workInProgress.stateNode = instance;

Expand Down
Loading

0 comments on commit 19092ac

Please sign in to comment.