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
Pieter De Baets committed
Sep 30, 2025 at 12:34 UTC
ef8894452b826f905d69e61435c6f2c30731bfa6
11 files changed
+18
-43
packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js
+12
-3
@@ -287,7 +287,6 @@ describe('ReactFabric', () => {
287
expect(nativeFabricUIManager.completeRoot).toBeCalled();
288
});
289
290
- // @gate enablePersistedModeClonedFlag
290
it('should not clone nodes when layout effects are used', async () => {
291
const View = createReactNativeComponentClass('RCTView', () => ({
292
validAttributes: {foo: true},
@@ -305,6 +304,8 @@ describe('ReactFabric', () => {
304
<ComponentWithEffect />
305
</View>,
306
11,
307
+ null,
308
+ true,
309
),
310
);
311
expect(nativeFabricUIManager.completeRoot).toBeCalled();
@@ -316,6 +317,8 @@ describe('ReactFabric', () => {
317
<ComponentWithEffect />
318
</View>,
319
11,
320
+ null,
321
+ true,
322
),
323
);
324
expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
@@ -327,7 +330,6 @@ describe('ReactFabric', () => {
330
expect(nativeFabricUIManager.completeRoot).not.toBeCalled();
331
});
332
330
- // @gate enablePersistedModeClonedFlag
333
it('should not clone nodes when insertion effects are used', async () => {
334
const View = createReactNativeComponentClass('RCTView', () => ({
335
validAttributes: {foo: true},
@@ -345,6 +347,8 @@ describe('ReactFabric', () => {
347
<ComponentWithRef />
348
</View>,
349
11,
350
+ null,
351
+ true,
352
),
353
);
354
expect(nativeFabricUIManager.completeRoot).toBeCalled();
@@ -356,6 +360,8 @@ describe('ReactFabric', () => {
360
<ComponentWithRef />
361
</View>,
362
11,
363
+ null,
364
+ true,
365
),
366
);
367
expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
@@ -367,7 +373,6 @@ describe('ReactFabric', () => {
373
expect(nativeFabricUIManager.completeRoot).not.toBeCalled();
374
});
375
370
- // @gate enablePersistedModeClonedFlag
376
it('should not clone nodes when useImperativeHandle is used', async () => {
377
const View = createReactNativeComponentClass('RCTView', () => ({
378
validAttributes: {foo: true},
@@ -387,6 +392,8 @@ describe('ReactFabric', () => {
392
<ComponentWithImperativeHandle ref={ref} />
393
</View>,
394
11,
395
+ null,
396
+ true,
397
),
398
);
399
expect(nativeFabricUIManager.completeRoot).toBeCalled();
@@ -399,6 +406,8 @@ describe('ReactFabric', () => {
406
<ComponentWithImperativeHandle ref={ref} />
407
</View>,
408
11,
409
+ null,
410
+ true,
411
),
412
);
413
expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
packages/react-reconciler/src/ReactFiberCommitWork.js
+1
-5
@@ -48,7 +48,6 @@ import {
48
alwaysThrottleRetries,
49
enableCreateEventHandleAPI,
50
enableHiddenSubtreeInsertionEffectCleanup,
51
- enablePersistedModeClonedFlag,
51
enableProfilerTimer,
52
enableProfilerCommitHooks,
53
enableSuspenseCallback,
@@ -1969,10 +1968,7 @@ function recursivelyTraverseMutationEffects(
1968
}
1969
}
1970
1972
- if (
1973
- parentFiber.subtreeFlags &
1974
- (enablePersistedModeClonedFlag ? MutationMask | Cloned : MutationMask)
1975
- ) {
1971
+ if (parentFiber.subtreeFlags & (MutationMask | Cloned)) {
1972
let child = parentFiber.child;
1973
while (child !== null) {
1974
commitMutationEffectsOnFiber(child, root, lanes);
packages/react-reconciler/src/ReactFiberCompleteWork.js
+5
-21
@@ -35,7 +35,6 @@ import {
35
enableLegacyHidden,
36
enableSuspenseCallback,
37
enableScopeAPI,
38
- enablePersistedModeClonedFlag,
38
enableProfilerTimer,
39
enableTransitionTracing,
40
passChildrenWhenCloningPersistedNodes,
@@ -92,7 +91,6 @@ import {
91
Snapshot,
92
ChildDeletion,
93
StaticMask,
95
- MutationMask,
94
Passive,
95
ForceClientRender,
96
MaySuspendCommit,
@@ -205,7 +203,7 @@ function markUpdate(workInProgress: Fiber) {
203
* it received an update that requires a clone of the tree above.
204
*/
205
function markCloned(workInProgress: Fiber) {
208
- if (supportsPersistence && enablePersistedModeClonedFlag) {
206
+ if (supportsPersistence) {
207
workInProgress.flags |= Cloned;
208
}
209
}
@@ -227,9 +225,7 @@ function doesRequireClone(current: null | Fiber, completedWork: Fiber) {
225
// then we only have to check the `completedWork.subtreeFlags`.
226
let child = completedWork.child;
227
while (child !== null) {
230
- const checkedFlags = enablePersistedModeClonedFlag
231
- ? Cloned | Visibility | Placement | ChildDeletion
232
- : MutationMask;
228
+ const checkedFlags = Cloned | Visibility | Placement | ChildDeletion;
229
if (
230
(child.flags & checkedFlags) !== NoFlags ||
231
(child.subtreeFlags & checkedFlags) !== NoFlags
@@ -526,16 +522,9 @@ function updateHostComponent(
522
markUpdate(workInProgress);
523
}
524
workInProgress.stateNode = newInstance;
529
- if (!requiresClone) {
530
- if (!enablePersistedModeClonedFlag) {
531
- // If there are no other effects in this tree, we need to flag this node as having one.
532
- // Even though we're not going to use it for anything.
533
- // Otherwise parents won't know that there are new children to propagate upwards.
534
- markUpdate(workInProgress);
535
- }
536
- } else if (
537
- !passChildrenWhenCloningPersistedNodes ||
538
- hasOffscreenComponentChild
525
+ if (
526
+ requiresClone &&
527
+ (!passChildrenWhenCloningPersistedNodes || hasOffscreenComponentChild)
528
) {
529
// If children have changed, we have to add them all to the set.
530
appendAllChildren(
@@ -693,11 +682,6 @@ function updateHostText(
682
currentHostContext,
683
workInProgress,
684
);
696
- if (!enablePersistedModeClonedFlag) {
697
- // We'll have to mark it as having an effect, even though we won't use the effect for anything.
698
- // This lets the parents know that at least one of their children has changed.
699
- markUpdate(workInProgress);
700
- }
685
} else {
686
workInProgress.stateNode = current.stateNode;
687
}
packages/shared/ReactFeatureFlags.js
-6
@@ -129,12 +129,6 @@ export const alwaysThrottleRetries: boolean = true;
129
130
export const passChildrenWhenCloningPersistedNodes: boolean = false;
131
132
-/**
133
- * Enables a new Fiber flag used in persisted mode to reduce the number
134
- * of cloned host components.
135
- */
136
-export const enablePersistedModeClonedFlag: boolean = false;
137
-
132
export const enableEagerAlternateStateNodeCleanup: boolean = true;
133
134
/**
packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js
-1
@@ -20,7 +20,6 @@
20
export const alwaysThrottleRetries = __VARIANT__;
21
export const enableObjectFiber = __VARIANT__;
22
export const enableHiddenSubtreeInsertionEffectCleanup = __VARIANT__;
23
-export const enablePersistedModeClonedFlag = __VARIANT__;
23
export const enableEagerAlternateStateNodeCleanup = __VARIANT__;
24
export const passChildrenWhenCloningPersistedNodes = __VARIANT__;
25
export const renameElementSymbol = __VARIANT__;
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -22,7 +22,6 @@ export const {
22
alwaysThrottleRetries,
23
enableHiddenSubtreeInsertionEffectCleanup,
24
enableObjectFiber,
25
- enablePersistedModeClonedFlag,
25
enableEagerAlternateStateNodeCleanup,
26
passChildrenWhenCloningPersistedNodes,
27
renameElementSymbol,
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -38,7 +38,6 @@ export const enableLegacyFBSupport: boolean = false;
38
export const enableLegacyHidden: boolean = false;
39
export const enableNoCloningMemoCache: boolean = false;
40
export const enableObjectFiber: boolean = false;
41
-export const enablePersistedModeClonedFlag: boolean = false;
41
export const enablePostpone: boolean = false;
42
export const enableReactTestRendererWarning: boolean = false;
43
export const enableRetryLaneExpiration: boolean = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -53,7 +53,6 @@ export const enableFizzExternalRuntime: boolean = true;
53
export const alwaysThrottleRetries: boolean = true;
54
55
export const passChildrenWhenCloningPersistedNodes: boolean = false;
56
-export const enablePersistedModeClonedFlag: boolean = false;
56
export const disableClientCache: boolean = true;
57
58
export const enableInfiniteRenderLoopDetection: boolean = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js
-1
@@ -33,7 +33,6 @@ export const enableLegacyFBSupport = false;
33
export const enableLegacyHidden = false;
34
export const enableNoCloningMemoCache = false;
35
export const enableObjectFiber = false;
36
-export const enablePersistedModeClonedFlag = false;
36
export const enablePostpone = false;
37
export const enableProfilerCommitHooks = __PROFILE__;
38
export const enableProfilerNestedUpdatePhase = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -54,7 +54,6 @@ export const enableFizzExternalRuntime: boolean = false;
54
export const alwaysThrottleRetries: boolean = true;
55
56
export const passChildrenWhenCloningPersistedNodes: boolean = false;
57
-export const enablePersistedModeClonedFlag: boolean = false;
57
export const disableClientCache: boolean = true;
58
59
export const enableInfiniteRenderLoopDetection: boolean = false;
packages/shared/forks/ReactFeatureFlags.www.js
-2
@@ -93,8 +93,6 @@ export const enableFizzExternalRuntime: boolean = true;
93
94
export const passChildrenWhenCloningPersistedNodes: boolean = false;
95
96
-export const enablePersistedModeClonedFlag: boolean = false;
97
-
96
export const disableClientCache: boolean = true;
97
98
export const enableReactTestRendererWarning: boolean = false;