Skip to content

Commit a19627e

Browse files
committed
[Fabric] Pass children when cloning (#27458)
## Summary Currently when cloning nodes in Fabric, we reset a node's children on each clone, and then repeatedly call appendChild to restore the previous list of children (even if it was quasi-identical to before). This causes unnecessary invalidation of the layout state in Fabric's ShadowNode data (which in turn may require additional yoga clones) and extra JSI calls. This PR adds a feature flag to pass in the children as part of the clone call, so Fabric always has a complete view of the node that's being mutated. This feature flag requires matching changes in the react-native repo: facebook/react-native#39817 ## How did you test this change? Unit test added demonstrates the new behaviour ``` yarn test -r www-modern ReactFabric-test yarn test ReactFabric-test.internal ``` Tested a manual sync into React Native and verified core surfaces render correctly. DiffTrain build for commit 151e75a.
1 parent c6c6388 commit a19627e

File tree

11 files changed

+241
-126
lines changed

11 files changed

+241
-126
lines changed

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-dev.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<99f1fded76af1fea6984fd8b08571351>>
10+
* @generated SignedSource<<73b93e8e6858b35c538fb675ac42495a>>
1111
*/
1212

1313
'use strict';
@@ -16335,7 +16335,8 @@ function completeWork(current, workInProgress, renderLanes) {
1633516335
_rootContainerInstance,
1633616336
_currentHostContext,
1633716337
workInProgress
16338-
);
16338+
); // TODO: For persistent renderers, we should pass children as part
16339+
// of the initial instance creation
1633916340

1634016341
appendAllChildren(_instance3, workInProgress);
1634116342
workInProgress.stateNode = _instance3; // Certain renderers require commit-time effects for initial mount.
@@ -24772,7 +24773,7 @@ function createFiberRoot(
2477224773
return root;
2477324774
}
2477424775

24775-
var ReactVersion = "18.3.0-canary-dddfe6882-20231005";
24776+
var ReactVersion = "18.3.0-canary-151e75a12-20231010";
2477624777

2477724778
// Might add PROFILE later.
2477824779

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-prod.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8978,7 +8978,7 @@ var devToolsConfig$jscomp$inline_998 = {
89788978
throw Error("TestRenderer does not support findFiberByHostInstance()");
89798979
},
89808980
bundleType: 0,
8981-
version: "18.3.0-canary-dddfe6882-20231005",
8981+
version: "18.3.0-canary-151e75a12-20231010",
89828982
rendererPackageName: "react-test-renderer"
89838983
};
89848984
var internals$jscomp$inline_1191 = {
@@ -9009,7 +9009,7 @@ var internals$jscomp$inline_1191 = {
90099009
scheduleRoot: null,
90109010
setRefreshHandler: null,
90119011
getCurrentFiber: null,
9012-
reconcilerVersion: "18.3.0-canary-dddfe6882-20231005"
9012+
reconcilerVersion: "18.3.0-canary-151e75a12-20231010"
90139013
};
90149014
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
90159015
var hook$jscomp$inline_1192 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-profiling.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9404,7 +9404,7 @@ var devToolsConfig$jscomp$inline_1040 = {
94049404
throw Error("TestRenderer does not support findFiberByHostInstance()");
94059405
},
94069406
bundleType: 0,
9407-
version: "18.3.0-canary-dddfe6882-20231005",
9407+
version: "18.3.0-canary-151e75a12-20231010",
94089408
rendererPackageName: "react-test-renderer"
94099409
};
94109410
var internals$jscomp$inline_1232 = {
@@ -9435,7 +9435,7 @@ var internals$jscomp$inline_1232 = {
94359435
scheduleRoot: null,
94369436
setRefreshHandler: null,
94379437
getCurrentFiber: null,
9438-
reconcilerVersion: "18.3.0-canary-dddfe6882-20231005"
9438+
reconcilerVersion: "18.3.0-canary-151e75a12-20231010"
94399439
};
94409440
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
94419441
var hook$jscomp$inline_1233 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-dev.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ if (
2727
}
2828
"use strict";
2929

30-
var ReactVersion = "18.3.0-canary-dddfe6882-20231005";
30+
var ReactVersion = "18.3.0-canary-151e75a12-20231010";
3131

3232
// ATTENTION
3333
// When adding new symbols to this file,

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-prod.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,4 +616,4 @@ exports.useSyncExternalStore = function (
616616
exports.useTransition = function () {
617617
return ReactCurrentDispatcher.current.useTransition();
618618
};
619-
exports.version = "18.3.0-canary-dddfe6882-20231005";
619+
exports.version = "18.3.0-canary-151e75a12-20231010";

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/cjs/React-profiling.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ exports.useSyncExternalStore = function (
619619
exports.useTransition = function () {
620620
return ReactCurrentDispatcher.current.useTransition();
621621
};
622-
exports.version = "18.3.0-canary-dddfe6882-20231005";
622+
exports.version = "18.3.0-canary-151e75a12-20231010";
623623

624624
/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
625625
if (
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
dddfe688206dafa5646550d351eb9a8e9c53654a
1+
151e75a128d0fd436dce365335b96c5686f704d4

compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-dev.fb.js

Lines changed: 89 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<d547d784dfd2ff8ab9a6adadb432b1cc>>
10+
* @generated SignedSource<<f5489af768a0de079885e316e09faadd>>
1111
*/
1212

1313
'use strict';
@@ -3180,7 +3180,9 @@ var enableUseRefAccessWarning = dynamicFlags.enableUseRefAccessWarning,
31803180
dynamicFlags.enableDeferRootSchedulingToMicrotask,
31813181
alwaysThrottleRetries = dynamicFlags.alwaysThrottleRetries,
31823182
useMicrotasksForSchedulingInFabric =
3183-
dynamicFlags.useMicrotasksForSchedulingInFabric; // The rest of the flags are static for better dead code elimination.
3183+
dynamicFlags.useMicrotasksForSchedulingInFabric,
3184+
passChildrenWhenCloningPersistedNodes =
3185+
dynamicFlags.passChildrenWhenCloningPersistedNodes; // The rest of the flags are static for better dead code elimination.
31843186
var enableSchedulingProfiler = true;
31853187
var enableProfilerTimer = true;
31863188
var enableProfilerCommitHooks = true;
@@ -5007,9 +5009,8 @@ function cloneInstance(
50075009
type,
50085010
oldProps,
50095011
newProps,
5010-
internalInstanceHandle,
50115012
keepChildren,
5012-
recyclableInstance
5013+
newChildSet
50135014
) {
50145015
var viewConfig = instance.canonical.viewConfig;
50155016
var updatePayload = diff(oldProps, newProps, viewConfig.validAttributes); // TODO: If the event handlers have changed, we need to update the current props
@@ -5028,10 +5029,23 @@ function cloneInstance(
50285029
return instance;
50295030
}
50305031
} else {
5031-
if (updatePayload !== null) {
5032-
clone = cloneNodeWithNewChildrenAndProps(node, updatePayload);
5032+
// If passChildrenWhenCloningPersistedNodes is enabled, children will be non-null
5033+
if (newChildSet != null) {
5034+
if (updatePayload !== null) {
5035+
clone = cloneNodeWithNewChildrenAndProps(
5036+
node,
5037+
newChildSet,
5038+
updatePayload
5039+
);
5040+
} else {
5041+
clone = cloneNodeWithNewChildren(node, newChildSet);
5042+
}
50335043
} else {
5034-
clone = cloneNodeWithNewChildren(node);
5044+
if (updatePayload !== null) {
5045+
clone = cloneNodeWithNewChildrenAndProps(node, updatePayload);
5046+
} else {
5047+
clone = cloneNodeWithNewChildren(node);
5048+
}
50355049
}
50365050
}
50375051

@@ -5040,7 +5054,7 @@ function cloneInstance(
50405054
canonical: instance.canonical
50415055
};
50425056
}
5043-
function cloneHiddenInstance(instance, type, props, internalInstanceHandle) {
5057+
function cloneHiddenInstance(instance, type, props) {
50445058
var viewConfig = instance.canonical.viewConfig;
50455059
var node = instance.node;
50465060
var updatePayload = create(
@@ -5056,19 +5070,29 @@ function cloneHiddenInstance(instance, type, props, internalInstanceHandle) {
50565070
canonical: instance.canonical
50575071
};
50585072
}
5059-
function cloneHiddenTextInstance(instance, text, internalInstanceHandle) {
5073+
function cloneHiddenTextInstance(instance, text) {
50605074
throw new Error("Not yet implemented.");
50615075
}
5062-
function createContainerChildSet(container) {
5063-
return createChildNodeSet(container);
5076+
function createContainerChildSet() {
5077+
if (passChildrenWhenCloningPersistedNodes) {
5078+
return [];
5079+
} else {
5080+
return createChildNodeSet();
5081+
}
50645082
}
50655083
function appendChildToContainerChildSet(childSet, child) {
5066-
appendChildNodeToSet(childSet, child.node);
5084+
if (passChildrenWhenCloningPersistedNodes) {
5085+
childSet.push(child.node);
5086+
} else {
5087+
appendChildNodeToSet(childSet, child.node);
5088+
}
50675089
}
50685090
function finalizeContainerChildren(container, newChildren) {
50695091
completeRoot(container, newChildren);
50705092
}
5071-
function replaceContainerChildren(container, newChildren) {}
5093+
function replaceContainerChildren(container, newChildren) {
5094+
// Noop - children will be replaced in finalizeContainerChildren
5095+
}
50725096
function preloadInstance(type, props) {
50735097
return true;
50745098
}
@@ -18734,7 +18758,6 @@ function appendAllChildren(
1873418758
var _node = workInProgress.child;
1873518759

1873618760
while (_node !== null) {
18737-
// eslint-disable-next-line no-labels
1873818761
if (_node.tag === HostComponent) {
1873918762
var instance = _node.stateNode;
1874018763

@@ -18764,7 +18787,14 @@ function appendAllChildren(
1876418787
child.return = _node;
1876518788
}
1876618789

18767-
appendAllChildren(parent, _node, true, true);
18790+
appendAllChildren(
18791+
parent,
18792+
_node,
18793+
/* needsVisibilityToggle */
18794+
true,
18795+
/* isHidden */
18796+
true
18797+
);
1876818798
} else if (_node.child !== null) {
1876918799
_node.child.return = _node;
1877018800
_node = _node.child;
@@ -18836,7 +18866,9 @@ function appendAllChildrenToContainer(
1883618866
appendAllChildrenToContainer(
1883718867
containerChildSet,
1883818868
node,
18869+
/* needsVisibilityToggle */
1883918870
_needsVisibilityToggle,
18871+
/* isHidden */
1884018872
true
1884118873
);
1884218874
} else if (node.child !== null) {
@@ -18874,9 +18906,16 @@ function updateHostContainer(current, workInProgress) {
1887418906
if (childrenUnchanged);
1887518907
else {
1887618908
var container = portalOrRoot.containerInfo;
18877-
var newChildSet = createContainerChildSet(container); // If children might have changed, we have to add them all to the set.
18909+
var newChildSet = createContainerChildSet(); // If children might have changed, we have to add them all to the set.
1887818910

18879-
appendAllChildrenToContainer(newChildSet, workInProgress, false, false);
18911+
appendAllChildrenToContainer(
18912+
newChildSet,
18913+
workInProgress,
18914+
/* needsVisibilityToggle */
18915+
false,
18916+
/* isHidden */
18917+
false
18918+
);
1888018919
portalOrRoot.pendingChildren = newChildSet; // Schedule an update on the container to swap out the container.
1888118920

1888218921
markUpdate(workInProgress);
@@ -18905,22 +18944,38 @@ function updateHostComponent(
1890518944
workInProgress.stateNode = currentInstance;
1890618945
return;
1890718946
}
18947+
1890818948
getHostContext();
18949+
var newChildSet = null;
18950+
18951+
if (!childrenUnchanged && passChildrenWhenCloningPersistedNodes) {
18952+
newChildSet = createContainerChildSet(); // If children might have changed, we have to add them all to the set.
18953+
18954+
appendAllChildrenToContainer(
18955+
newChildSet,
18956+
workInProgress,
18957+
/* needsVisibilityToggle */
18958+
false,
18959+
/* isHidden */
18960+
false
18961+
);
18962+
}
18963+
1890918964
var newInstance = cloneInstance(
1891018965
currentInstance,
1891118966
type,
1891218967
_oldProps,
1891318968
newProps,
18914-
workInProgress,
18915-
childrenUnchanged
18969+
childrenUnchanged,
18970+
newChildSet
1891618971
);
1891718972

1891818973
if (newInstance === currentInstance) {
1891918974
// No changes, just reuse the existing instance.
1892018975
// Note that this might release a previous clone.
1892118976
workInProgress.stateNode = currentInstance;
1892218977
return;
18923-
}
18978+
} // Certain renderers require commit-time effects for initial mount.
1892418979

1892518980
workInProgress.stateNode = newInstance;
1892618981

@@ -18929,9 +18984,16 @@ function updateHostComponent(
1892918984
// Even though we're not going to use it for anything.
1893018985
// Otherwise parents won't know that there are new children to propagate upwards.
1893118986
markUpdate(workInProgress);
18932-
} else {
18987+
} else if (!passChildrenWhenCloningPersistedNodes) {
1893318988
// If children might have changed, we have to add them all to the set.
18934-
appendAllChildren(newInstance, workInProgress, false, false);
18989+
appendAllChildren(
18990+
newInstance,
18991+
workInProgress,
18992+
/* needsVisibilityToggle */
18993+
false,
18994+
/* isHidden */
18995+
false
18996+
);
1893518997
}
1893618998
}
1893718999
} // This function must be called at the very end of the complete phase, because
@@ -19399,7 +19461,8 @@ function completeWork(current, workInProgress, renderLanes) {
1939919461
_rootContainerInstance,
1940019462
_currentHostContext,
1940119463
workInProgress
19402-
);
19464+
); // TODO: For persistent renderers, we should pass children as part
19465+
// of the initial instance creation
1940319466

1940419467
appendAllChildren(_instance3, workInProgress, false, false);
1940519468
workInProgress.stateNode = _instance3; // Certain renderers require commit-time effects for initial mount.
@@ -21092,9 +21155,7 @@ function detachFiberAfterEffects(fiber) {
2109221155
}
2109321156

2109421157
function emptyPortalContainer(current) {
21095-
var portal = current.stateNode;
21096-
var containerInfo = portal.containerInfo;
21097-
createContainerChildSet(containerInfo);
21158+
createContainerChildSet();
2109821159
}
2109921160

2110021161
function commitPlacement(finishedWork) {
@@ -21167,7 +21228,7 @@ function commitDeletionEffectsOnFiber(
2116721228

2116821229
case HostPortal: {
2116921230
{
21170-
emptyPortalContainer(deletedFiber);
21231+
emptyPortalContainer();
2117121232
recursivelyTraverseDeletionEffects(
2117221233
finishedRoot,
2117321234
nearestMountedAncestor,
@@ -27063,7 +27124,7 @@ function createFiberRoot(
2706327124
return root;
2706427125
}
2706527126

27066-
var ReactVersion = "18.3.0-canary-112791ea";
27127+
var ReactVersion = "18.3.0-canary-4b68b914";
2706727128

2706827129
function createPortal$1(
2706927130
children,

0 commit comments

Comments
 (0)