From 6c2bfb2fc9e8c76606effbd2db4f413cc82a6132 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Tue, 3 Aug 2021 18:56:10 -0400 Subject: [PATCH] Re-add old Fabric Offscreen impl behind flag There's a chance that #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. --- .../src/ReactFabricHostConfig.js | 26 ++++ .../src/createReactNoop.js | 48 ++++++ .../src/ReactFiberBeginWork.new.js | 11 +- .../src/ReactFiberBeginWork.old.js | 11 +- .../src/ReactFiberCompleteWork.new.js | 144 +++++++++--------- .../src/ReactFiberCompleteWork.old.js | 144 +++++++++--------- .../ReactFiberHostConfigWithNoPersistence.js | 2 + .../src/ReactFiberThrow.new.js | 3 +- .../src/ReactFiberThrow.old.js | 3 +- .../src/forks/ReactFiberHostConfig.custom.js | 2 + packages/shared/ReactFeatureFlags.js | 2 + .../forks/ReactFeatureFlags.native-fb.js | 2 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.native.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.testing.js | 1 + .../forks/ReactFeatureFlags.testing.www.js | 1 + .../forks/ReactFeatureFlags.www-dynamic.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 2 + 20 files changed, 257 insertions(+), 150 deletions(-) diff --git a/packages/react-native-renderer/src/ReactFabricHostConfig.js b/packages/react-native-renderer/src/ReactFabricHostConfig.js index ac7fb64d922cd..7b2d1f5a13665 100644 --- a/packages/react-native-renderer/src/ReactFabricHostConfig.js +++ b/packages/react-native-renderer/src/ReactFabricHostConfig.js @@ -457,6 +457,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); } diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index bc31a1c7c3cae..9e2c1ba7f876c 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -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); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index a687e9d16315d..c39702aa47490 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -89,6 +89,7 @@ import { enableLazyContextPropagation, enableSuspenseLayoutEffectSemantics, enableSchedulingProfiler, + enablePersistentOffscreenHostContainer, } from 'shared/ReactFeatureFlags'; import invariant from 'shared/invariant'; import isArray from 'shared/isArray'; @@ -744,7 +745,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 @@ -760,12 +761,10 @@ function updateOffscreenComponent( renderLanes, ); return offscreenContainer; - } - if (supportsMutation) { + } else { reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; } - return null; } function reconcileOffscreenHostContainer( @@ -2383,7 +2382,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. @@ -2411,7 +2410,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. diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index a1153497c5cde..2af3356c0220b 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -89,6 +89,7 @@ import { enableLazyContextPropagation, enableSuspenseLayoutEffectSemantics, enableSchedulingProfiler, + enablePersistentOffscreenHostContainer, } from 'shared/ReactFeatureFlags'; import invariant from 'shared/invariant'; import isArray from 'shared/isArray'; @@ -744,7 +745,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 @@ -760,12 +761,10 @@ function updateOffscreenComponent( renderLanes, ); return offscreenContainer; - } - if (supportsMutation) { + } else { reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; } - return null; } function reconcileOffscreenHostContainer( @@ -2383,7 +2382,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. @@ -2411,7 +2410,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. diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js index fea034edaa67f..fd88dade0c163 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js @@ -84,6 +84,8 @@ import { supportsMutation, supportsPersistence, cloneInstance, + cloneHiddenInstance, + cloneHiddenTextInstance, createContainerChildSet, appendChildToContainerChildSet, finalizeContainerChildren, @@ -128,6 +130,7 @@ import { enableProfilerTimer, enableCache, enableSuspenseLayoutEffectSemantics, + enablePersistentOffscreenHostContainer, } from 'shared/ReactFeatureFlags'; import { renderDidSuspend, @@ -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; @@ -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; @@ -327,6 +366,8 @@ 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. @@ -334,15 +375,41 @@ if (supportsMutation) { 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; @@ -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); @@ -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( @@ -690,65 +757,6 @@ function bubbleProperties(completedWork: Fiber) { return didBailout; } -export function completeSuspendedOffscreenHostContainer( - current: Fiber | null, - workInProgress: Fiber, -) { - // This is a fork of the complete phase for HostComponent. We use it when - // a suspense tree is in its fallback state, because in that case the primary - // tree that includes the offscreen boundary is skipped over without a - // regular complete phase. - // - // We can optimize this path further by inlining the update logic for - // offscreen instances specifically, i.e. skipping the `prepareUpdate` call. - const rootContainerInstance = getRootHostContainer(); - const type = workInProgress.type; - const newProps = workInProgress.memoizedProps; - if (current !== null) { - updateHostComponent( - current, - workInProgress, - type, - newProps, - rootContainerInstance, - ); - } else { - const currentHostContext = getHostContext(); - const instance = createInstance( - type, - newProps, - rootContainerInstance, - currentHostContext, - workInProgress, - ); - - appendAllChildren(instance, workInProgress); - - workInProgress.stateNode = instance; - - // Certain renderers require commit-time effects for initial mount. - // (eg DOM renderer supports auto-focus for certain elements). - // Make sure such renderers get scheduled for later work. - if ( - finalizeInitialChildren( - instance, - type, - newProps, - rootContainerInstance, - currentHostContext, - ) - ) { - markUpdate(workInProgress); - } - - if (workInProgress.ref !== null) { - // If there is a ref on a host node we need to schedule a callback - markRef(workInProgress); - } - } - bubbleProperties(workInProgress); -} - function completeWork( current: Fiber | null, workInProgress: Fiber, @@ -869,7 +877,7 @@ function completeWork( workInProgress, ); - appendAllChildren(instance, workInProgress); + appendAllChildren(instance, workInProgress, false, false); workInProgress.stateNode = instance; diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js index 4658938c9d5d4..9a5510356d626 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js @@ -84,6 +84,8 @@ import { supportsMutation, supportsPersistence, cloneInstance, + cloneHiddenInstance, + cloneHiddenTextInstance, createContainerChildSet, appendChildToContainerChildSet, finalizeContainerChildren, @@ -128,6 +130,7 @@ import { enableProfilerTimer, enableCache, enableSuspenseLayoutEffectSemantics, + enablePersistentOffscreenHostContainer, } from 'shared/ReactFeatureFlags'; import { renderDidSuspend, @@ -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; @@ -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; @@ -327,6 +366,8 @@ 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. @@ -334,15 +375,41 @@ if (supportsMutation) { 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; @@ -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); @@ -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( @@ -690,65 +757,6 @@ function bubbleProperties(completedWork: Fiber) { return didBailout; } -export function completeSuspendedOffscreenHostContainer( - current: Fiber | null, - workInProgress: Fiber, -) { - // This is a fork of the complete phase for HostComponent. We use it when - // a suspense tree is in its fallback state, because in that case the primary - // tree that includes the offscreen boundary is skipped over without a - // regular complete phase. - // - // We can optimize this path further by inlining the update logic for - // offscreen instances specifically, i.e. skipping the `prepareUpdate` call. - const rootContainerInstance = getRootHostContainer(); - const type = workInProgress.type; - const newProps = workInProgress.memoizedProps; - if (current !== null) { - updateHostComponent( - current, - workInProgress, - type, - newProps, - rootContainerInstance, - ); - } else { - const currentHostContext = getHostContext(); - const instance = createInstance( - type, - newProps, - rootContainerInstance, - currentHostContext, - workInProgress, - ); - - appendAllChildren(instance, workInProgress); - - workInProgress.stateNode = instance; - - // Certain renderers require commit-time effects for initial mount. - // (eg DOM renderer supports auto-focus for certain elements). - // Make sure such renderers get scheduled for later work. - if ( - finalizeInitialChildren( - instance, - type, - newProps, - rootContainerInstance, - currentHostContext, - ) - ) { - markUpdate(workInProgress); - } - - if (workInProgress.ref !== null) { - // If there is a ref on a host node we need to schedule a callback - markRef(workInProgress); - } - } - bubbleProperties(workInProgress); -} - function completeWork( current: Fiber | null, workInProgress: Fiber, @@ -869,7 +877,7 @@ function completeWork( workInProgress, ); - appendAllChildren(instance, workInProgress); + appendAllChildren(instance, workInProgress, false, false); workInProgress.stateNode = instance; diff --git a/packages/react-reconciler/src/ReactFiberHostConfigWithNoPersistence.js b/packages/react-reconciler/src/ReactFiberHostConfigWithNoPersistence.js index 2dd44342a399c..824cdeacc73fd 100644 --- a/packages/react-reconciler/src/ReactFiberHostConfigWithNoPersistence.js +++ b/packages/react-reconciler/src/ReactFiberHostConfigWithNoPersistence.js @@ -30,3 +30,5 @@ export const finalizeContainerChildren = shim; export const replaceContainerChildren = shim; export const getOffscreenContainerType = shim; export const getOffscreenContainerProps = shim; +export const cloneHiddenInstance = shim; +export const cloneHiddenTextInstance = shim; diff --git a/packages/react-reconciler/src/ReactFiberThrow.new.js b/packages/react-reconciler/src/ReactFiberThrow.new.js index 79b0ed6bc39ee..67ac620e80d87 100644 --- a/packages/react-reconciler/src/ReactFiberThrow.new.js +++ b/packages/react-reconciler/src/ReactFiberThrow.new.js @@ -44,6 +44,7 @@ import { enableSchedulingProfiler, enableLazyContextPropagation, enableUpdaterTracking, + enablePersistentOffscreenHostContainer, } from 'shared/ReactFeatureFlags'; import {createCapturedValue} from './ReactCapturedValue'; import { @@ -321,7 +322,7 @@ function throwException( // all lifecycle effect tags. sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); - if (supportsPersistence) { + if (supportsPersistence && enablePersistentOffscreenHostContainer) { // Another legacy Suspense quirk. In persistent mode, if this is the // initial mount, override the props of the host container to hide // its contents. diff --git a/packages/react-reconciler/src/ReactFiberThrow.old.js b/packages/react-reconciler/src/ReactFiberThrow.old.js index 9c2df828f817e..6309e20d9e622 100644 --- a/packages/react-reconciler/src/ReactFiberThrow.old.js +++ b/packages/react-reconciler/src/ReactFiberThrow.old.js @@ -44,6 +44,7 @@ import { enableSchedulingProfiler, enableLazyContextPropagation, enableUpdaterTracking, + enablePersistentOffscreenHostContainer, } from 'shared/ReactFeatureFlags'; import {createCapturedValue} from './ReactCapturedValue'; import { @@ -321,7 +322,7 @@ function throwException( // all lifecycle effect tags. sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); - if (supportsPersistence) { + if (supportsPersistence && enablePersistentOffscreenHostContainer) { // Another legacy Suspense quirk. In persistent mode, if this is the // initial mount, override the props of the host container to hide // its contents. diff --git a/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js index 9dbaa47eb7110..1cc10a78cb013 100644 --- a/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js @@ -131,6 +131,8 @@ export const getOffscreenContainerType = $$$hostConfig.getOffscreenContainerType; export const getOffscreenContainerProps = $$$hostConfig.getOffscreenContainerProps; +export const cloneHiddenInstance = $$$hostConfig.cloneHiddenInstance; +export const cloneHiddenTextInstance = $$$hostConfig.cloneHiddenTextInstance; // ------------------- // Hydration diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index a1689f64a5d8e..6df5af24d953e 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -173,3 +173,5 @@ export const enableLazyContextPropagation = false; export const enableSyncDefaultUpdates = true; export const allowConcurrentByDefault = false; + +export const enablePersistentOffscreenHostContainer = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 9e52536fbb36d..8d0a2256ec7d2 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -60,6 +60,8 @@ export const disableSchedulerTimeoutInWorkLoop = false; export const enableLazyContextPropagation = false; export const enableSyncDefaultUpdates = true; export const allowConcurrentByDefault = true; +// TODO: Import this from internal ReactNativeFeatureFlags instead +export const enablePersistentOffscreenHostContainer = __EXPERIMENTAL__; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index b34b583f18861..5bcc6097447ab 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false; export const enableLazyContextPropagation = false; export const enableSyncDefaultUpdates = true; export const allowConcurrentByDefault = false; +export const enablePersistentOffscreenHostContainer = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 7357217b16912..4a897ec77e46f 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false; export const enableLazyContextPropagation = false; export const enableSyncDefaultUpdates = true; export const allowConcurrentByDefault = false; +export const enablePersistentOffscreenHostContainer = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index 10a05e52e152a..48df0a41a08b4 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false; export const enableLazyContextPropagation = false; export const enableSyncDefaultUpdates = true; export const allowConcurrentByDefault = true; +export const enablePersistentOffscreenHostContainer = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 617ec8f0bc186..481cf6b023ac4 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false; export const enableLazyContextPropagation = false; export const enableSyncDefaultUpdates = true; export const allowConcurrentByDefault = true; +export const enablePersistentOffscreenHostContainer = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index 50e1c73f8dd20..48971f129b7b1 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false; export const enableLazyContextPropagation = false; export const enableSyncDefaultUpdates = true; export const allowConcurrentByDefault = false; +export const enablePersistentOffscreenHostContainer = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index d16f6212c5485..c11a7757fa3c6 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false; export const enableLazyContextPropagation = false; export const enableSyncDefaultUpdates = true; export const allowConcurrentByDefault = true; +export const enablePersistentOffscreenHostContainer = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 591193ebb777b..72ae2f2c5b479 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -59,3 +59,4 @@ export const disableSchedulerTimeoutInWorkLoop = __VARIANT__; export const enableLazyContextPropagation = __VARIANT__; export const enableSyncDefaultUpdates = __VARIANT__; export const allowConcurrentByDefault = true; +export const enablePersistentOffscreenHostContainer = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index dadf7972612f5..d514e15e8827d 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -94,6 +94,8 @@ export const allowConcurrentByDefault = true; export const deletedTreeCleanUpLevel = 3; +export const enablePersistentOffscreenHostContainer = false; + // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars type Check<_X, Y: _X, X: Y = _X> = null;