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

View File

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

View File

@ -35,7 +35,6 @@ import {
enableLegacyHidden,
enableSuspenseCallback,
enableScopeAPI,
enablePersistedModeClonedFlag,
enableProfilerTimer,
enableTransitionTracing,
passChildrenWhenCloningPersistedNodes,
@ -92,7 +91,6 @@ import {
Snapshot,
ChildDeletion,
StaticMask,
MutationMask,
Passive,
ForceClientRender,
MaySuspendCommit,
@ -205,7 +203,7 @@ function markUpdate(workInProgress: Fiber) {
* it received an update that requires a clone of the tree above.
*/
function markCloned(workInProgress: Fiber) {
if (supportsPersistence && enablePersistedModeClonedFlag) {
if (supportsPersistence) {
workInProgress.flags |= Cloned;
}
}
@ -227,9 +225,7 @@ function doesRequireClone(current: null | Fiber, completedWork: Fiber) {
// then we only have to check the `completedWork.subtreeFlags`.
let child = completedWork.child;
while (child !== null) {
const checkedFlags = enablePersistedModeClonedFlag
? Cloned | Visibility | Placement | ChildDeletion
: MutationMask;
const checkedFlags = Cloned | Visibility | Placement | ChildDeletion;
if (
(child.flags & checkedFlags) !== NoFlags ||
(child.subtreeFlags & checkedFlags) !== NoFlags
@ -526,16 +522,9 @@ function updateHostComponent(
markUpdate(workInProgress);
}
workInProgress.stateNode = newInstance;
if (!requiresClone) {
if (!enablePersistedModeClonedFlag) {
// If there are no other effects in this tree, we need to flag this node as having one.
// 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 (
requiresClone &&
(!passChildrenWhenCloningPersistedNodes || hasOffscreenComponentChild)
) {
// If children have changed, we have to add them all to the set.
appendAllChildren(
@ -693,11 +682,6 @@ function updateHostText(
currentHostContext,
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 {
workInProgress.stateNode = current.stateNode;
}

View File

@ -129,12 +129,6 @@ export const alwaysThrottleRetries: boolean = true;
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;
/**

View File

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

View File

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

View File

@ -38,7 +38,6 @@ export const enableLegacyFBSupport: boolean = false;
export const enableLegacyHidden: boolean = false;
export const enableNoCloningMemoCache: boolean = false;
export const enableObjectFiber: boolean = false;
export const enablePersistedModeClonedFlag: boolean = false;
export const enablePostpone: boolean = false;
export const enableReactTestRendererWarning: 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 passChildrenWhenCloningPersistedNodes: boolean = false;
export const enablePersistedModeClonedFlag: boolean = false;
export const disableClientCache: boolean = true;
export const enableInfiniteRenderLoopDetection: boolean = false;

View File

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

View File

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

View File

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