react-native-macos/ReactCommon/fabric/mounting/TreeStateReconciliation.cpp

95 строки
3.5 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "TreeStateReconciliation.h"
namespace facebook {
namespace react {
using ChangedShadowNodePairs = std::vector<
std::pair<ShadowNode::Shared const &, ShadowNode::Unshared const &>>;
/**
* Clones any children in the subtree that need to be cloned, and adds those to
* the `changedPairs` vector argument.
*/
static ChangedShadowNodePairs reconcileStateWithChildren(
SharedShadowNodeList const &newChildren,
SharedShadowNodeList const &oldChildren) {
ChangedShadowNodePairs changedPairs;
// Find children that are the same family in both trees.
// We only want to find nodes that existing in the new tree - if they
// don't exist in the new tree, they're being deleted; if they don't exist
// in the old tree, they're new. We don't need to deal with either of those
// cases here.
// Currently we use a naive double loop - this could be improved, but we need
// to be able to handle cases where nodes are entirely reordered, for
// instance.
for (auto const &child : newChildren) {
auto const oldChild = std::find_if(
oldChildren.begin(), oldChildren.end(), [&](const auto &el) {
return ShadowNode::sameFamily(*child, *el);
});
if (oldChild != oldChildren.end()) {
UnsharedShadowNode newChild =
reconcileStateWithTree(child.get(), *oldChild);
if (newChild != nullptr) {
changedPairs.push_back(std::make_pair(child, newChild));
}
}
};
return changedPairs;
}
UnsharedShadowNode reconcileStateWithTree(
ShadowNode const *newNode,
SharedShadowNode committedNode) {
// If the revisions on the node are the same, we can finish here.
// Subtrees are guaranteed to be identical at this point, too.
if (committedNode->getStateRevision() <= newNode->getStateRevision()) {
return nullptr;
}
// If we got this fair, we're guaranteed that the state of 1) this node,
// and/or 2) some descendant node is out-of-date and must be reconciled.
// This requires traversing all children, and we must at *least* clone
// this node, whether or not we clone and update any children.
auto const &newChildren = newNode->getChildren();
auto const &oldChildren = committedNode->getChildren();
auto const changedPairs =
reconcileStateWithChildren(newChildren, oldChildren);
ShadowNode::SharedListOfShared clonedChildren =
ShadowNodeFragment::childrenPlaceholder();
// If any children were cloned, we need to recreate the child list.
// This won't cause any children to be cloned that weren't already cloned -
// it just collects all children, cloned or uncloned, into a new list.
if (!changedPairs.empty()) {
ShadowNode::UnsharedListOfShared newList =
std::make_shared<ShadowNode::ListOfShared>();
for (std::size_t i = 0, j = 0; i < newChildren.size(); ++i) {
if (j < changedPairs.size() && changedPairs[j].first == newChildren[i]) {
newList->push_back(changedPairs[j].second);
++j;
} else {
newList->push_back(newChildren[i]);
}
}
clonedChildren = newList;
}
return newNode->clone({/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .children = */ clonedChildren,
/* .state = */ newNode->getMostRecentState()});
}
} // namespace react
} // namespace facebook