From 0ec77d8d08792a5f18938257c06ef4487c48f982 Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Wed, 12 Nov 2025 05:29:25 -0800 Subject: [PATCH] Calll the ShadowNode::cloneMultiple callback for each cloned node (#54431) Summary: The original intention by the cloneMultiple method was to give the possibility of defining the cloning method only for the nodes definied by the list of families. But this way it is impossible to for example set the value of `runtimeShadowNodeReference` in the fragment, so instead we now pass the responsibility of cloning to the user for all the cloned nodes. # Changelog [General] [Changed] - cloneMultiple now invokes the callback for every cloned node, instead of doing that only for the nodes in `familiesToUpdate` Reviewed By: javache Differential Revision: D85852143 --- .../react/renderer/core/ShadowNode.cpp | 14 +++----- .../renderer/core/tests/ShadowNodeTest.cpp | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp index 7b894673cdbf..f8383851616e 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp @@ -412,7 +412,6 @@ namespace { std::shared_ptr cloneMultipleRecursive( const ShadowNode& shadowNode, - const std::unordered_set& familiesToUpdate, const std::unordered_map& childrenCount, const std::function(const ShadowNode&, const ShadowNodeFragment&)>& callback) { @@ -430,16 +429,12 @@ std::shared_ptr cloneMultipleRecursive( std::make_shared>>( children); } - (*newChildren)[i] = cloneMultipleRecursive( - *children[i], familiesToUpdate, childrenCount, callback); + (*newChildren)[i] = + cloneMultipleRecursive(*children[i], childrenCount, callback); } } - ShadowNodeFragment fragment{.children = newChildren}; - if (familiesToUpdate.contains(family)) { - return callback(shadowNode, fragment); - } - return shadowNode.clone(fragment); + return callback(shadowNode, {.children = newChildren}); } } // namespace @@ -479,8 +474,7 @@ std::shared_ptr ShadowNode::cloneMultiple( return nullptr; } - return cloneMultipleRecursive( - *this, familiesToUpdate, childrenCount, callback); + return cloneMultipleRecursive(*this, childrenCount, callback); } #pragma mark - DebugStringConvertible diff --git a/packages/react-native/ReactCommon/react/renderer/core/tests/ShadowNodeTest.cpp b/packages/react-native/ReactCommon/react/renderer/core/tests/ShadowNodeTest.cpp index a6ea8da1d62f..2ac02a75c3e2 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/tests/ShadowNodeTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/tests/ShadowNodeTest.cpp @@ -342,3 +342,35 @@ TEST_F(ShadowNodeTest, cloneMultiple) { EXPECT_EQ(newNodeABA->getTag(), nodeABA_->getTag()); EXPECT_EQ(newNodeABA.get(), nodeABA_.get()); } + +TEST_F(ShadowNodeTest, cloneMultipleWithSingleFamily) { + auto newProps = std::make_shared(); + auto newRoot = nodeA_->cloneMultiple( + {&nodeAB_->getFamily()}, + [&](const ShadowNode& oldShadowNode, const ShadowNodeFragment& fragment) { + return oldShadowNode.clone({ + .props = newProps, + .children = fragment.children, + .state = fragment.state, + }); + }); + + EXPECT_EQ(newRoot->getTag(), nodeA_->getTag()); + // The callback is called for each cloned node, so the root props are also + // updated + EXPECT_EQ(newRoot->getProps(), newProps); + + auto newNodeAA = newRoot->getChildren()[0]; + EXPECT_EQ(newNodeAA->getTag(), nodeAA_->getTag()); + EXPECT_EQ(newNodeAA->getProps(), nodeAA_->getProps()); + // AA was cloned when its parent was cloned as it was shared + EXPECT_NE(newNodeAA.get(), nodeAA_.get()); + + auto newNodeAB = newRoot->getChildren()[1]; + EXPECT_EQ(newNodeAB->getTag(), nodeAB_->getTag()); + EXPECT_EQ(newNodeAB->getProps(), newProps); + + auto newNodeABA = newNodeAB->getChildren()[0]; + EXPECT_EQ(newNodeABA->getTag(), nodeABA_->getTag()); + EXPECT_EQ(newNodeABA.get(), nodeABA_.get()); +}