@samitouri / QOS-React-2 / commits / 19092ac8c3

Re-add old Fabric Offscreen impl behind flag (#22018)

* 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. * 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.

Andrew Clark committed Aug 3, 2021 at 22:30 UTC 19092ac8c354b92c2e0e27b73f391571ad452505
25 files changed +324 -44
packages/react-native-renderer/src/ReactFabricHostConfig.js
+26
@@ -460,6 +460,32 @@ export function getOffscreenContainerProps(
460 }
461 }
462
463 +export function cloneHiddenInstance(
464 + instance: Instance,
465 + type: string,
466 + props: Props,
467 + internalInstanceHandle: Object,
468 +): Instance {
469 + const viewConfig = instance.canonical.viewConfig;
470 + const node = instance.node;
471 + const updatePayload = create(
472 + {style: {display: 'none'}},
473 + viewConfig.validAttributes,
474 + );
475 + return {
476 + node: cloneNodeWithNewProps(node, updatePayload),
477 + canonical: instance.canonical,
478 + };
479 +}
480 +
481 +export function cloneHiddenTextInstance(
482 + instance: Instance,
483 + text: string,
484 + internalInstanceHandle: Object,
485 +): TextInstance {
486 + throw new Error('Not yet implemented.');
487 +}
488 +
489 export function createContainerChildSet(container: Container): ChildSet {
490 return createChildNodeSet(container);
491 }
packages/react-noop-renderer/src/createReactNoop.js
+48
@@ -582,6 +582,54 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
582 children,
583 };
584 },
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 + },
633 };
634
635 const NoopRenderer = reconciler(hostConfig);
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+5 -7
@@ -89,6 +89,7 @@ import {
89 enableLazyContextPropagation,
90 enableSuspenseLayoutEffectSemantics,
91 enableSchedulingProfiler,
92 + enablePersistentOffscreenHostContainer,
93 } from 'shared/ReactFeatureFlags';
94 import invariant from 'shared/invariant';
95 import isArray from 'shared/isArray';
@@ -146,7 +147,6 @@ import {
147 registerSuspenseInstanceRetry,
148 supportsHydration,
149 isPrimaryRenderer,
149 - supportsMutation,
150 supportsPersistence,
151 getOffscreenContainerProps,
152 } from './ReactFiberHostConfig';
@@ -744,7 +744,7 @@ function updateOffscreenComponent(
744 workInProgress.updateQueue = spawnedCachePool;
745 }
746
747 - if (supportsPersistence) {
747 + if (enablePersistentOffscreenHostContainer && supportsPersistence) {
748 // In persistent mode, the offscreen children are wrapped in a host node.
749 // TODO: Optimize this to use the OffscreenComponent fiber instead of
750 // an extra HostComponent fiber. Need to make sure this doesn't break Fabric
@@ -760,12 +760,10 @@ function updateOffscreenComponent(
760 renderLanes,
761 );
762 return offscreenContainer;
763 - }
764 - if (supportsMutation) {
763 + } else {
764 reconcileChildren(current, workInProgress, nextChildren, renderLanes);
765 return workInProgress.child;
766 }
768 - return null;
767 }
768
769 function reconcileOffscreenHostContainer(
@@ -2383,7 +2381,7 @@ function updateSuspenseFallbackChildren(
2381 currentPrimaryChildFragment.treeBaseDuration;
2382 }
2383
2386 - if (supportsPersistence) {
2384 + if (enablePersistentOffscreenHostContainer && supportsPersistence) {
2385 // In persistent mode, the offscreen children are wrapped in a host node.
2386 // We need to complete it now, because we're going to skip over its normal
2387 // complete phase and go straight to rendering the fallback.
@@ -2411,7 +2409,7 @@ function updateSuspenseFallbackChildren(
2409 primaryChildProps,
2410 );
2411
2414 - if (supportsPersistence) {
2412 + if (enablePersistentOffscreenHostContainer && supportsPersistence) {
2413 // In persistent mode, the offscreen children are wrapped in a host node.
2414 // We need to complete it now, because we're going to skip over its normal
2415 // complete phase and go straight to rendering the fallback.
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+5 -7
@@ -89,6 +89,7 @@ import {
89 enableLazyContextPropagation,
90 enableSuspenseLayoutEffectSemantics,
91 enableSchedulingProfiler,
92 + enablePersistentOffscreenHostContainer,
93 } from 'shared/ReactFeatureFlags';
94 import invariant from 'shared/invariant';
95 import isArray from 'shared/isArray';
@@ -146,7 +147,6 @@ import {
147 registerSuspenseInstanceRetry,
148 supportsHydration,
149 isPrimaryRenderer,
149 - supportsMutation,
150 supportsPersistence,
151 getOffscreenContainerProps,
152 } from './ReactFiberHostConfig';
@@ -744,7 +744,7 @@ function updateOffscreenComponent(
744 workInProgress.updateQueue = spawnedCachePool;
745 }
746
747 - if (supportsPersistence) {
747 + if (enablePersistentOffscreenHostContainer && supportsPersistence) {
748 // In persistent mode, the offscreen children are wrapped in a host node.
749 // TODO: Optimize this to use the OffscreenComponent fiber instead of
750 // an extra HostComponent fiber. Need to make sure this doesn't break Fabric
@@ -760,12 +760,10 @@ function updateOffscreenComponent(
760 renderLanes,
761 );
762 return offscreenContainer;
763 - }
764 - if (supportsMutation) {
763 + } else {
764 reconcileChildren(current, workInProgress, nextChildren, renderLanes);
765 return workInProgress.child;
766 }
768 - return null;
767 }
768
769 function reconcileOffscreenHostContainer(
@@ -2383,7 +2381,7 @@ function updateSuspenseFallbackChildren(
2381 currentPrimaryChildFragment.treeBaseDuration;
2382 }
2383
2386 - if (supportsPersistence) {
2384 + if (enablePersistentOffscreenHostContainer && supportsPersistence) {
2385 // In persistent mode, the offscreen children are wrapped in a host node.
2386 // We need to complete it now, because we're going to skip over its normal
2387 // complete phase and go straight to rendering the fallback.
@@ -2411,7 +2409,7 @@ function updateSuspenseFallbackChildren(
2409 primaryChildProps,
2410 );
2411
2414 - if (supportsPersistence) {
2412 + if (enablePersistentOffscreenHostContainer && supportsPersistence) {
2413 // In persistent mode, the offscreen children are wrapped in a host node.
2414 // We need to complete it now, because we're going to skip over its normal
2415 // complete phase and go straight to rendering the fallback.
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+77 -10
@@ -84,6 +84,8 @@ import {
84 supportsMutation,
85 supportsPersistence,
86 cloneInstance,
87 + cloneHiddenInstance,
88 + cloneHiddenTextInstance,
89 createContainerChildSet,
90 appendChildToContainerChildSet,
91 finalizeContainerChildren,
@@ -128,6 +130,7 @@ import {
130 enableProfilerTimer,
131 enableCache,
132 enableSuspenseLayoutEffectSemantics,
133 + enablePersistentOffscreenHostContainer,
134 } from 'shared/ReactFeatureFlags';
135 import {
136 renderDidSuspend,
@@ -198,7 +201,12 @@ let updateHostText;
201 if (supportsMutation) {
202 // Mutation mode
203
201 - appendAllChildren = function(parent: Instance, workInProgress: Fiber) {
204 + appendAllChildren = function(
205 + parent: Instance,
206 + workInProgress: Fiber,
207 + needsVisibilityToggle: boolean,
208 + isHidden: boolean,
209 + ) {
210 // We only have the top Fiber that was created but we need recurse down its
211 // children to find all the terminal nodes.
212 let node = workInProgress.child;
@@ -286,22 +294,53 @@ if (supportsMutation) {
294 } else if (supportsPersistence) {
295 // Persistent host tree mode
296
289 - appendAllChildren = function(parent: Instance, workInProgress: Fiber) {
297 + appendAllChildren = function(
298 + parent: Instance,
299 + workInProgress: Fiber,
300 + needsVisibilityToggle: boolean,
301 + isHidden: boolean,
302 + ) {
303 // We only have the top Fiber that was created but we need recurse down its
304 // children to find all the terminal nodes.
305 let node = workInProgress.child;
306 while (node !== null) {
307 // eslint-disable-next-line no-labels
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 + }
316 appendInitialChild(parent, instance);
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 + }
324 appendInitialChild(parent, instance);
325 } else if (node.tag === HostPortal) {
326 // If we have a portal child, then we don't want to traverse
327 // down its children. Instead, we'll get insertions from each child in
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 + }
344 } else if (node.child !== null) {
345 node.child.return = node;
346 node = node.child;
@@ -327,6 +366,8 @@ if (supportsMutation) {
366 const appendAllChildrenToContainer = function(
367 containerChildSet: ChildSet,
368 workInProgress: Fiber,
369 + needsVisibilityToggle: boolean,
370 + isHidden: boolean,
371 ) {
372 // We only have the top Fiber that was created but we need recurse down its
373 // children to find all the terminal nodes.
@@ -334,15 +375,41 @@ if (supportsMutation) {
375 while (node !== null) {
376 // eslint-disable-next-line no-labels
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 + }
385 appendChildToContainerChildSet(containerChildSet, instance);
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 + }
393 appendChildToContainerChildSet(containerChildSet, instance);
394 } else if (node.tag === HostPortal) {
395 // If we have a portal child, then we don't want to traverse
396 // down its children. Instead, we'll get insertions from each child in
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 + }
413 } else if (node.child !== null) {
414 node.child.return = node;
415 node = node.child;
@@ -376,7 +443,7 @@ if (supportsMutation) {
443 const container = portalOrRoot.containerInfo;
444 const newChildSet = createContainerChildSet(container);
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);
447 portalOrRoot.pendingChildren = newChildSet;
448 // Schedule an update on the container to swap out the container.
449 markUpdate(workInProgress);
@@ -449,7 +516,7 @@ if (supportsMutation) {
516 markUpdate(workInProgress);
517 } else {
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);
520 }
521 };
522 updateHostText = function(
@@ -722,7 +789,7 @@ export function completeSuspendedOffscreenHostContainer(
789 workInProgress,
790 );
791
725 - appendAllChildren(instance, workInProgress);
792 + appendAllChildren(instance, workInProgress, false, false);
793
794 workInProgress.stateNode = instance;
795
@@ -869,7 +936,7 @@ function completeWork(
936 workInProgress,
937 );
938
872 - appendAllChildren(instance, workInProgress);
939 + appendAllChildren(instance, workInProgress, false, false);
940
941 workInProgress.stateNode = instance;
942
packages/react-reconciler/src/ReactFiberCompleteWork.old.js
+77 -10
@@ -84,6 +84,8 @@ import {
84 supportsMutation,
85 supportsPersistence,
86 cloneInstance,
87 + cloneHiddenInstance,
88 + cloneHiddenTextInstance,
89 createContainerChildSet,
90 appendChildToContainerChildSet,
91 finalizeContainerChildren,
@@ -128,6 +130,7 @@ import {
130 enableProfilerTimer,
131 enableCache,
132 enableSuspenseLayoutEffectSemantics,
133 + enablePersistentOffscreenHostContainer,
134 } from 'shared/ReactFeatureFlags';
135 import {
136 renderDidSuspend,
@@ -198,7 +201,12 @@ let updateHostText;
201 if (supportsMutation) {
202 // Mutation mode
203
201 - appendAllChildren = function(parent: Instance, workInProgress: Fiber) {
204 + appendAllChildren = function(
205 + parent: Instance,
206 + workInProgress: Fiber,
207 + needsVisibilityToggle: boolean,
208 + isHidden: boolean,
209 + ) {
210 // We only have the top Fiber that was created but we need recurse down its
211 // children to find all the terminal nodes.
212 let node = workInProgress.child;
@@ -286,22 +294,53 @@ if (supportsMutation) {
294 } else if (supportsPersistence) {
295 // Persistent host tree mode
296
289 - appendAllChildren = function(parent: Instance, workInProgress: Fiber) {
297 + appendAllChildren = function(
298 + parent: Instance,
299 + workInProgress: Fiber,
300 + needsVisibilityToggle: boolean,
301 + isHidden: boolean,
302 + ) {
303 // We only have the top Fiber that was created but we need recurse down its
304 // children to find all the terminal nodes.
305 let node = workInProgress.child;
306 while (node !== null) {
307 // eslint-disable-next-line no-labels
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 + }
316 appendInitialChild(parent, instance);
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 + }
324 appendInitialChild(parent, instance);
325 } else if (node.tag === HostPortal) {
326 // If we have a portal child, then we don't want to traverse
327 // down its children. Instead, we'll get insertions from each child in
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 + }
344 } else if (node.child !== null) {
345 node.child.return = node;
346 node = node.child;
@@ -327,6 +366,8 @@ if (supportsMutation) {
366 const appendAllChildrenToContainer = function(
367 containerChildSet: ChildSet,
368 workInProgress: Fiber,
369 + needsVisibilityToggle: boolean,
370 + isHidden: boolean,
371 ) {
372 // We only have the top Fiber that was created but we need recurse down its
373 // children to find all the terminal nodes.
@@ -334,15 +375,41 @@ if (supportsMutation) {
375 while (node !== null) {
376 // eslint-disable-next-line no-labels
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 + }
385 appendChildToContainerChildSet(containerChildSet, instance);
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 + }
393 appendChildToContainerChildSet(containerChildSet, instance);
394 } else if (node.tag === HostPortal) {
395 // If we have a portal child, then we don't want to traverse
396 // down its children. Instead, we'll get insertions from each child in
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 + }
413 } else if (node.child !== null) {
414 node.child.return = node;
415 node = node.child;
@@ -376,7 +443,7 @@ if (supportsMutation) {
443 const container = portalOrRoot.containerInfo;
444 const newChildSet = createContainerChildSet(container);
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);
447 portalOrRoot.pendingChildren = newChildSet;
448 // Schedule an update on the container to swap out the container.
449 markUpdate(workInProgress);
@@ -449,7 +516,7 @@ if (supportsMutation) {
516 markUpdate(workInProgress);
517 } else {
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);
520 }
521 };
522 updateHostText = function(
@@ -722,7 +789,7 @@ export function completeSuspendedOffscreenHostContainer(
789 workInProgress,
790 );
791
725 - appendAllChildren(instance, workInProgress);
792 + appendAllChildren(instance, workInProgress, false, false);
793
794 workInProgress.stateNode = instance;
795
@@ -869,7 +936,7 @@ function completeWork(
936 workInProgress,
937 );
938
872 - appendAllChildren(instance, workInProgress);
939 + appendAllChildren(instance, workInProgress, false, false);
940
941 workInProgress.stateNode = instance;
942
packages/react-reconciler/src/ReactFiberHostConfigWithNoPersistence.js
+2
@@ -30,3 +30,5 @@ export const finalizeContainerChildren = shim;
30 export const replaceContainerChildren = shim;
31 export const getOffscreenContainerType = shim;
32 export const getOffscreenContainerProps = shim;
33 +export const cloneHiddenInstance = shim;
34 +export const cloneHiddenTextInstance = shim;
packages/react-reconciler/src/ReactFiberThrow.new.js
+2 -1
@@ -44,6 +44,7 @@ import {
44 enableSchedulingProfiler,
45 enableLazyContextPropagation,
46 enableUpdaterTracking,
47 + enablePersistentOffscreenHostContainer,
48 } from 'shared/ReactFeatureFlags';
49 import {createCapturedValue} from './ReactCapturedValue';
50 import {
@@ -321,7 +322,7 @@ function throwException(
322 // all lifecycle effect tags.
323 sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);
324
324 - if (supportsPersistence) {
325 + if (supportsPersistence && enablePersistentOffscreenHostContainer) {
326 // Another legacy Suspense quirk. In persistent mode, if this is the
327 // initial mount, override the props of the host container to hide
328 // its contents.
packages/react-reconciler/src/ReactFiberThrow.old.js
+2 -1
@@ -44,6 +44,7 @@ import {
44 enableSchedulingProfiler,
45 enableLazyContextPropagation,
46 enableUpdaterTracking,
47 + enablePersistentOffscreenHostContainer,
48 } from 'shared/ReactFeatureFlags';
49 import {createCapturedValue} from './ReactCapturedValue';
50 import {
@@ -321,7 +322,7 @@ function throwException(
322 // all lifecycle effect tags.
323 sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);
324
324 - if (supportsPersistence) {
325 + if (supportsPersistence && enablePersistentOffscreenHostContainer) {
326 // Another legacy Suspense quirk. In persistent mode, if this is the
327 // initial mount, override the props of the host container to hide
328 // its contents.
packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js
+2
@@ -131,6 +131,8 @@ export const getOffscreenContainerType =
131 $$$hostConfig.getOffscreenContainerType;
132 export const getOffscreenContainerProps =
133 $$$hostConfig.getOffscreenContainerProps;
134 +export const cloneHiddenInstance = $$$hostConfig.cloneHiddenInstance;
135 +export const cloneHiddenTextInstance = $$$hostConfig.cloneHiddenTextInstance;
136
137 // -------------------
138 // Hydration
packages/shared/ReactFeatureFlags.js
+2
@@ -173,3 +173,5 @@ export const enableLazyContextPropagation = false;
173 export const enableSyncDefaultUpdates = true;
174
175 export const allowConcurrentByDefault = false;
176 +
177 +export const enablePersistentOffscreenHostContainer = false;
packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js new
+29
@@ -0,0 +1,29 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow strict
8 + */
9 +
10 +import typeof * as ExportsType from './ReactFeatureFlags.native-fb-dynamic';
11 +import typeof * as DynamicFlagsType from 'ReactNativeInternalFeatureFlags';
12 +
13 +// In xplat, these flags are controlled by GKs. Because most GKs have some
14 +// population running in either mode, we should run our tests that way, too,
15 +//
16 +// Use __VARIANT__ to simulate a GK. The tests will be run twice: once
17 +// with the __VARIANT__ set to `true`, and once set to `false`.
18 +//
19 +// TODO: __VARIANT__ isn't supported for React Native flags yet. You can set the
20 +// flag here but it won't be set to `true` in any of our test runs. Need to
21 +// update the test configuration.
22 +
23 +export const enablePersistentOffscreenHostContainer = __VARIANT__;
24 +
25 +// Flow magic to verify the exports of this file match the original version.
26 +// eslint-disable-next-line no-unused-vars
27 +type Check<_X, Y: _X, X: Y = _X> = null;
28 +// eslint-disable-next-line no-unused-expressions
29 +(null: Check<ExportsType, DynamicFlagsType>);
packages/shared/forks/ReactFeatureFlags.native-fb.js
+8
@@ -10,6 +10,14 @@
10 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
11 import typeof * as ExportsType from './ReactFeatureFlags.native-fb';
12
13 +// Re-export dynamic flags from the internal module. Intentionally using *
14 +// because this import is compiled to a `require` call.
15 +import * as dynamicFlags from 'ReactNativeInternalFeatureFlags';
16 +
17 +// We destructure each value before re-exporting to avoid a dynamic look-up on
18 +// the exports object every time a flag is read.
19 +export const {enablePersistentOffscreenHostContainer} = dynamicFlags;
20 +
21 // The rest of the flags are static for better dead code elimination.
22 export const enableDebugTracing = false;
23 export const enableSchedulingProfiler = false;
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableLazyContextPropagation = false;
60 export const enableSyncDefaultUpdates = true;
61 export const allowConcurrentByDefault = false;
62 +export const enablePersistentOffscreenHostContainer = false;
63
64 // Flow magic to verify the exports of this file match the original version.
65 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableLazyContextPropagation = false;
60 export const enableSyncDefaultUpdates = true;
61 export const allowConcurrentByDefault = false;
62 +export const enablePersistentOffscreenHostContainer = false;
63
64 // Flow magic to verify the exports of this file match the original version.
65 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+1
@@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableLazyContextPropagation = false;
60 export const enableSyncDefaultUpdates = true;
61 export const allowConcurrentByDefault = true;
62 +export const enablePersistentOffscreenHostContainer = false;
63
64 // Flow magic to verify the exports of this file match the original version.
65 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableLazyContextPropagation = false;
60 export const enableSyncDefaultUpdates = true;
61 export const allowConcurrentByDefault = true;
62 +export const enablePersistentOffscreenHostContainer = false;
63
64 // Flow magic to verify the exports of this file match the original version.
65 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.testing.js
+1
@@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableLazyContextPropagation = false;
60 export const enableSyncDefaultUpdates = true;
61 export const allowConcurrentByDefault = false;
62 +export const enablePersistentOffscreenHostContainer = false;
63
64 // Flow magic to verify the exports of this file match the original version.
65 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.testing.www.js
+1
@@ -59,6 +59,7 @@ export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableLazyContextPropagation = false;
60 export const enableSyncDefaultUpdates = true;
61 export const allowConcurrentByDefault = true;
62 +export const enablePersistentOffscreenHostContainer = false;
63
64 // Flow magic to verify the exports of this file match the original version.
65 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+1
@@ -59,3 +59,4 @@ export const disableSchedulerTimeoutInWorkLoop = __VARIANT__;
59 export const enableLazyContextPropagation = __VARIANT__;
60 export const enableSyncDefaultUpdates = __VARIANT__;
61 export const allowConcurrentByDefault = true;
62 +export const enablePersistentOffscreenHostContainer = false;
packages/shared/forks/ReactFeatureFlags.www.js
+2
@@ -94,6 +94,8 @@ export const allowConcurrentByDefault = true;
94
95 export const deletedTreeCleanUpLevel = 3;
96
97 +export const enablePersistentOffscreenHostContainer = false;
98 +
99 // Flow magic to verify the exports of this file match the original version.
100 // eslint-disable-next-line no-unused-vars
101 type Check<_X, Y: _X, X: Y = _X> = null;
scripts/flow/config/flowconfig
+1
@@ -34,6 +34,7 @@
34 ./scripts/flow/react-devtools.js
35 ./scripts/flow/react-native-host-hooks.js
36 ./scripts/flow/react-relay-hooks.js
37 +./scripts/flow/xplat.js
38
39 [lints]
40 untyped-type-import=error
scripts/flow/xplat.js new
+12
@@ -0,0 +1,12 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +declare module 'ReactNativeInternalFeatureFlags' {
11 + declare export var enablePersistentOffscreenHostContainer: boolean;
12 +}
scripts/rollup/bundles.js
+15 -8
@@ -79,7 +79,7 @@ const bundles = [
79 moduleType: ISOMORPHIC,
80 entry: 'react',
81 global: 'React',
82 - externals: [],
82 + externals: ['ReactNativeInternalFeatureFlags'],
83 },
84
85 /******* Isomorphic Shared Subset *******/
@@ -105,7 +105,7 @@ const bundles = [
105 moduleType: ISOMORPHIC,
106 entry: 'react/jsx-runtime',
107 global: 'JSXRuntime',
108 - externals: ['react'],
108 + externals: ['react', 'ReactNativeInternalFeatureFlags'],
109 },
110
111 /******* React JSX DEV Runtime *******/
@@ -124,7 +124,7 @@ const bundles = [
124 moduleType: ISOMORPHIC,
125 entry: 'react/jsx-dev-runtime',
126 global: 'JSXDEVRuntime',
127 - externals: ['react'],
127 + externals: ['react', 'ReactNativeInternalFeatureFlags'],
128 },
129
130 /******* React Fetch Browser (experimental, new) *******/
@@ -372,6 +372,7 @@ const bundles = [
372 'react',
373 'ReactFlightNativeRelayServerIntegration',
374 'JSResourceReferenceImpl',
375 + 'ReactNativeInternalFeatureFlags',
376 ],
377 },
378
@@ -385,6 +386,7 @@ const bundles = [
386 'react',
387 'ReactFlightNativeRelayClientIntegration',
388 'JSResourceReferenceImpl',
389 + 'ReactNativeInternalFeatureFlags',
390 ],
391 },
392
@@ -432,7 +434,7 @@ const bundles = [
434 moduleType: RENDERER,
435 entry: 'react-native-renderer',
436 global: 'ReactNativeRenderer',
435 - externals: ['react-native'],
437 + externals: ['react-native', 'ReactNativeInternalFeatureFlags'],
438 babel: opts =>
439 Object.assign({}, opts, {
440 plugins: opts.plugins.concat([
@@ -462,7 +464,7 @@ const bundles = [
464 moduleType: RENDERER,
465 entry: 'react-native-renderer/fabric',
466 global: 'ReactFabric',
465 - externals: ['react-native'],
467 + externals: ['react-native', 'ReactNativeInternalFeatureFlags'],
468 babel: opts =>
469 Object.assign({}, opts, {
470 plugins: opts.plugins.concat([
@@ -499,7 +501,12 @@ const bundles = [
501 moduleType: RENDERER,
502 entry: 'react-test-renderer',
503 global: 'ReactTestRenderer',
502 - externals: ['react', 'scheduler', 'scheduler/unstable_mock'],
504 + externals: [
505 + 'react',
506 + 'scheduler',
507 + 'scheduler/unstable_mock',
508 + 'ReactNativeInternalFeatureFlags',
509 + ],
510 babel: opts =>
511 Object.assign({}, opts, {
512 plugins: opts.plugins.concat([
@@ -692,7 +699,7 @@ const bundles = [
699 moduleType: ISOMORPHIC,
700 entry: 'scheduler',
701 global: 'Scheduler',
695 - externals: [],
702 + externals: ['ReactNativeInternalFeatureFlags'],
703 },
704
705 /******* React Scheduler Mock (experimental) *******/
@@ -710,7 +717,7 @@ const bundles = [
717 moduleType: ISOMORPHIC,
718 entry: 'scheduler/unstable_mock',
719 global: 'SchedulerMock',
713 - externals: [],
720 + externals: ['ReactNativeInternalFeatureFlags'],
721 },
722
723 /******* React Scheduler Post Task (experimental) *******/
scripts/rollup/modules.js
+2
@@ -21,6 +21,7 @@ const importSideEffects = Object.freeze({
21 'react-fetch/node': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
22 'react-dom': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
23 url: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
24 + ReactNativeInternalFeatureFlags: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
25 });
26
27 // Bundles exporting globals that other modules rely on.
@@ -31,6 +32,7 @@ const knownGlobals = Object.freeze({
32 'react-interactions/events/tap': 'ReactEventsTap',
33 scheduler: 'Scheduler',
34 'scheduler/unstable_mock': 'SchedulerMock',
35 + ReactNativeInternalFeatureFlags: 'ReactNativeInternalFeatureFlags',
36 });
37
38 // Given ['react'] in bundle externals, returns { 'react': 'React' }.