Rollout enablePersistedModeClonedFlag (#34520)

## Summary

Experimentation has completed for this at Meta and we've observed
positive impact on key React Native surfaces.

## How did you test this change?

yarn flow fabric
This commit is contained in:
Pieter De Baets 2025-09-30 12:34:13 +01:00 committed by GitHub
parent e6f2a8a376
commit ef8894452b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 18 additions and 43 deletions

View File

@ -287,7 +287,6 @@ describe('ReactFabric', () => {
expect(nativeFabricUIManager.completeRoot).toBeCalled(); expect(nativeFabricUIManager.completeRoot).toBeCalled();
}); });
// @gate enablePersistedModeClonedFlag
it('should not clone nodes when layout effects are used', async () => { it('should not clone nodes when layout effects are used', async () => {
const View = createReactNativeComponentClass('RCTView', () => ({ const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true}, validAttributes: {foo: true},
@ -305,6 +304,8 @@ describe('ReactFabric', () => {
<ComponentWithEffect /> <ComponentWithEffect />
</View>, </View>,
11, 11,
null,
true,
), ),
); );
expect(nativeFabricUIManager.completeRoot).toBeCalled(); expect(nativeFabricUIManager.completeRoot).toBeCalled();
@ -316,6 +317,8 @@ describe('ReactFabric', () => {
<ComponentWithEffect /> <ComponentWithEffect />
</View>, </View>,
11, 11,
null,
true,
), ),
); );
expect(nativeFabricUIManager.cloneNode).not.toBeCalled(); expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
@ -327,7 +330,6 @@ describe('ReactFabric', () => {
expect(nativeFabricUIManager.completeRoot).not.toBeCalled(); expect(nativeFabricUIManager.completeRoot).not.toBeCalled();
}); });
// @gate enablePersistedModeClonedFlag
it('should not clone nodes when insertion effects are used', async () => { it('should not clone nodes when insertion effects are used', async () => {
const View = createReactNativeComponentClass('RCTView', () => ({ const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true}, validAttributes: {foo: true},
@ -345,6 +347,8 @@ describe('ReactFabric', () => {
<ComponentWithRef /> <ComponentWithRef />
</View>, </View>,
11, 11,
null,
true,
), ),
); );
expect(nativeFabricUIManager.completeRoot).toBeCalled(); expect(nativeFabricUIManager.completeRoot).toBeCalled();
@ -356,6 +360,8 @@ describe('ReactFabric', () => {
<ComponentWithRef /> <ComponentWithRef />
</View>, </View>,
11, 11,
null,
true,
), ),
); );
expect(nativeFabricUIManager.cloneNode).not.toBeCalled(); expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
@ -367,7 +373,6 @@ describe('ReactFabric', () => {
expect(nativeFabricUIManager.completeRoot).not.toBeCalled(); expect(nativeFabricUIManager.completeRoot).not.toBeCalled();
}); });
// @gate enablePersistedModeClonedFlag
it('should not clone nodes when useImperativeHandle is used', async () => { it('should not clone nodes when useImperativeHandle is used', async () => {
const View = createReactNativeComponentClass('RCTView', () => ({ const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true}, validAttributes: {foo: true},
@ -387,6 +392,8 @@ describe('ReactFabric', () => {
<ComponentWithImperativeHandle ref={ref} /> <ComponentWithImperativeHandle ref={ref} />
</View>, </View>,
11, 11,
null,
true,
), ),
); );
expect(nativeFabricUIManager.completeRoot).toBeCalled(); expect(nativeFabricUIManager.completeRoot).toBeCalled();
@ -399,6 +406,8 @@ describe('ReactFabric', () => {
<ComponentWithImperativeHandle ref={ref} /> <ComponentWithImperativeHandle ref={ref} />
</View>, </View>,
11, 11,
null,
true,
), ),
); );
expect(nativeFabricUIManager.cloneNode).not.toBeCalled(); expect(nativeFabricUIManager.cloneNode).not.toBeCalled();

View File

@ -48,7 +48,6 @@ import {
alwaysThrottleRetries, alwaysThrottleRetries,
enableCreateEventHandleAPI, enableCreateEventHandleAPI,
enableHiddenSubtreeInsertionEffectCleanup, enableHiddenSubtreeInsertionEffectCleanup,
enablePersistedModeClonedFlag,
enableProfilerTimer, enableProfilerTimer,
enableProfilerCommitHooks, enableProfilerCommitHooks,
enableSuspenseCallback, enableSuspenseCallback,
@ -1969,10 +1968,7 @@ function recursivelyTraverseMutationEffects(
} }
} }
if ( if (parentFiber.subtreeFlags & (MutationMask | Cloned)) {
parentFiber.subtreeFlags &
(enablePersistedModeClonedFlag ? MutationMask | Cloned : MutationMask)
) {
let child = parentFiber.child; let child = parentFiber.child;
while (child !== null) { while (child !== null) {
commitMutationEffectsOnFiber(child, root, lanes); commitMutationEffectsOnFiber(child, root, lanes);

View File

@ -35,7 +35,6 @@ import {
enableLegacyHidden, enableLegacyHidden,
enableSuspenseCallback, enableSuspenseCallback,
enableScopeAPI, enableScopeAPI,
enablePersistedModeClonedFlag,
enableProfilerTimer, enableProfilerTimer,
enableTransitionTracing, enableTransitionTracing,
passChildrenWhenCloningPersistedNodes, passChildrenWhenCloningPersistedNodes,
@ -92,7 +91,6 @@ import {
Snapshot, Snapshot,
ChildDeletion, ChildDeletion,
StaticMask, StaticMask,
MutationMask,
Passive, Passive,
ForceClientRender, ForceClientRender,
MaySuspendCommit, MaySuspendCommit,
@ -205,7 +203,7 @@ function markUpdate(workInProgress: Fiber) {
* it received an update that requires a clone of the tree above. * it received an update that requires a clone of the tree above.
*/ */
function markCloned(workInProgress: Fiber) { function markCloned(workInProgress: Fiber) {
if (supportsPersistence && enablePersistedModeClonedFlag) { if (supportsPersistence) {
workInProgress.flags |= Cloned; workInProgress.flags |= Cloned;
} }
} }
@ -227,9 +225,7 @@ function doesRequireClone(current: null | Fiber, completedWork: Fiber) {
// then we only have to check the `completedWork.subtreeFlags`. // then we only have to check the `completedWork.subtreeFlags`.
let child = completedWork.child; let child = completedWork.child;
while (child !== null) { while (child !== null) {
const checkedFlags = enablePersistedModeClonedFlag const checkedFlags = Cloned | Visibility | Placement | ChildDeletion;
? Cloned | Visibility | Placement | ChildDeletion
: MutationMask;
if ( if (
(child.flags & checkedFlags) !== NoFlags || (child.flags & checkedFlags) !== NoFlags ||
(child.subtreeFlags & checkedFlags) !== NoFlags (child.subtreeFlags & checkedFlags) !== NoFlags
@ -526,16 +522,9 @@ function updateHostComponent(
markUpdate(workInProgress); markUpdate(workInProgress);
} }
workInProgress.stateNode = newInstance; workInProgress.stateNode = newInstance;
if (!requiresClone) { if (
if (!enablePersistedModeClonedFlag) { requiresClone &&
// If there are no other effects in this tree, we need to flag this node as having one. (!passChildrenWhenCloningPersistedNodes || hasOffscreenComponentChild)
// Even though we're not going to use it for anything.
// Otherwise parents won't know that there are new children to propagate upwards.
markUpdate(workInProgress);
}
} else if (
!passChildrenWhenCloningPersistedNodes ||
hasOffscreenComponentChild
) { ) {
// If children have changed, we have to add them all to the set. // If children have changed, we have to add them all to the set.
appendAllChildren( appendAllChildren(
@ -693,11 +682,6 @@ function updateHostText(
currentHostContext, currentHostContext,
workInProgress, workInProgress,
); );
if (!enablePersistedModeClonedFlag) {
// We'll have to mark it as having an effect, even though we won't use the effect for anything.
// This lets the parents know that at least one of their children has changed.
markUpdate(workInProgress);
}
} else { } else {
workInProgress.stateNode = current.stateNode; workInProgress.stateNode = current.stateNode;
} }

View File

@ -129,12 +129,6 @@ export const alwaysThrottleRetries: boolean = true;
export const passChildrenWhenCloningPersistedNodes: boolean = false; export const passChildrenWhenCloningPersistedNodes: boolean = false;
/**
* Enables a new Fiber flag used in persisted mode to reduce the number
* of cloned host components.
*/
export const enablePersistedModeClonedFlag: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true; export const enableEagerAlternateStateNodeCleanup: boolean = true;
/** /**

View File

@ -20,7 +20,6 @@
export const alwaysThrottleRetries = __VARIANT__; export const alwaysThrottleRetries = __VARIANT__;
export const enableObjectFiber = __VARIANT__; export const enableObjectFiber = __VARIANT__;
export const enableHiddenSubtreeInsertionEffectCleanup = __VARIANT__; export const enableHiddenSubtreeInsertionEffectCleanup = __VARIANT__;
export const enablePersistedModeClonedFlag = __VARIANT__;
export const enableEagerAlternateStateNodeCleanup = __VARIANT__; export const enableEagerAlternateStateNodeCleanup = __VARIANT__;
export const passChildrenWhenCloningPersistedNodes = __VARIANT__; export const passChildrenWhenCloningPersistedNodes = __VARIANT__;
export const renameElementSymbol = __VARIANT__; export const renameElementSymbol = __VARIANT__;

View File

@ -22,7 +22,6 @@ export const {
alwaysThrottleRetries, alwaysThrottleRetries,
enableHiddenSubtreeInsertionEffectCleanup, enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber, enableObjectFiber,
enablePersistedModeClonedFlag,
enableEagerAlternateStateNodeCleanup, enableEagerAlternateStateNodeCleanup,
passChildrenWhenCloningPersistedNodes, passChildrenWhenCloningPersistedNodes,
renameElementSymbol, renameElementSymbol,

View File

@ -38,7 +38,6 @@ export const enableLegacyFBSupport: boolean = false;
export const enableLegacyHidden: boolean = false; export const enableLegacyHidden: boolean = false;
export const enableNoCloningMemoCache: boolean = false; export const enableNoCloningMemoCache: boolean = false;
export const enableObjectFiber: boolean = false; export const enableObjectFiber: boolean = false;
export const enablePersistedModeClonedFlag: boolean = false;
export const enablePostpone: boolean = false; export const enablePostpone: boolean = false;
export const enableReactTestRendererWarning: boolean = false; export const enableReactTestRendererWarning: boolean = false;
export const enableRetryLaneExpiration: boolean = false; export const enableRetryLaneExpiration: boolean = false;

View File

@ -53,7 +53,6 @@ export const enableFizzExternalRuntime: boolean = true;
export const alwaysThrottleRetries: boolean = true; export const alwaysThrottleRetries: boolean = true;
export const passChildrenWhenCloningPersistedNodes: boolean = false; export const passChildrenWhenCloningPersistedNodes: boolean = false;
export const enablePersistedModeClonedFlag: boolean = false;
export const disableClientCache: boolean = true; export const disableClientCache: boolean = true;
export const enableInfiniteRenderLoopDetection: boolean = false; export const enableInfiniteRenderLoopDetection: boolean = false;

View File

@ -33,7 +33,6 @@ export const enableLegacyFBSupport = false;
export const enableLegacyHidden = false; export const enableLegacyHidden = false;
export const enableNoCloningMemoCache = false; export const enableNoCloningMemoCache = false;
export const enableObjectFiber = false; export const enableObjectFiber = false;
export const enablePersistedModeClonedFlag = false;
export const enablePostpone = false; export const enablePostpone = false;
export const enableProfilerCommitHooks = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__;
export const enableProfilerNestedUpdatePhase = __PROFILE__; export const enableProfilerNestedUpdatePhase = __PROFILE__;

View File

@ -54,7 +54,6 @@ export const enableFizzExternalRuntime: boolean = false;
export const alwaysThrottleRetries: boolean = true; export const alwaysThrottleRetries: boolean = true;
export const passChildrenWhenCloningPersistedNodes: boolean = false; export const passChildrenWhenCloningPersistedNodes: boolean = false;
export const enablePersistedModeClonedFlag: boolean = false;
export const disableClientCache: boolean = true; export const disableClientCache: boolean = true;
export const enableInfiniteRenderLoopDetection: boolean = false; export const enableInfiniteRenderLoopDetection: boolean = false;

View File

@ -93,8 +93,6 @@ export const enableFizzExternalRuntime: boolean = true;
export const passChildrenWhenCloningPersistedNodes: boolean = false; export const passChildrenWhenCloningPersistedNodes: boolean = false;
export const enablePersistedModeClonedFlag: boolean = false;
export const disableClientCache: boolean = true; export const disableClientCache: boolean = true;
export const enableReactTestRendererWarning: boolean = false; export const enableReactTestRendererWarning: boolean = false;