TreeStateReconciliation.cpp 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #include "TreeStateReconciliation.h"
  8. namespace facebook {
  9. namespace react {
  10. using ChangedShadowNodePairs =
  11. std::vector<std::pair<ShadowNode::Shared, ShadowNode::Unshared>>;
  12. /**
  13. * Clones any children in the subtree that need to be cloned, and adds those to
  14. * the `changedPairs` vector argument.
  15. */
  16. static ChangedShadowNodePairs reconcileStateWithChildren(
  17. SharedShadowNodeList const &newChildren,
  18. SharedShadowNodeList const &oldChildren) {
  19. ChangedShadowNodePairs changedPairs;
  20. // Find children that are the same family in both trees.
  21. // We only want to find nodes that existing in the new tree - if they
  22. // don't exist in the new tree, they're being deleted; if they don't exist
  23. // in the old tree, they're new. We don't need to deal with either of those
  24. // cases here.
  25. // Currently we use a naive double loop - this could be improved, but we need
  26. // to be able to handle cases where nodes are entirely reordered, for
  27. // instance.
  28. for (auto const &child : newChildren) {
  29. auto const oldChild = std::find_if(
  30. oldChildren.begin(), oldChildren.end(), [&](const auto &el) {
  31. return ShadowNode::sameFamily(*child, *el);
  32. });
  33. if (oldChild != oldChildren.end()) {
  34. UnsharedShadowNode newChild =
  35. reconcileStateWithTree(child.get(), *oldChild);
  36. if (newChild != nullptr) {
  37. changedPairs.push_back(std::make_pair(child, newChild));
  38. }
  39. }
  40. };
  41. return changedPairs;
  42. }
  43. UnsharedShadowNode reconcileStateWithTree(
  44. ShadowNode const *newNode,
  45. SharedShadowNode committedNode) {
  46. // If the revisions on the node are the same, we can finish here.
  47. // Subtrees are guaranteed to be identical at this point, too.
  48. if (committedNode->getStateRevision() <= newNode->getStateRevision()) {
  49. return nullptr;
  50. }
  51. // If we got this fair, we're guaranteed that the state of 1) this node,
  52. // and/or 2) some descendant node is out-of-date and must be reconciled.
  53. // This requires traversing all children, and we must at *least* clone
  54. // this node, whether or not we clone and update any children.
  55. auto const &newChildren = newNode->getChildren();
  56. auto const &oldChildren = committedNode->getChildren();
  57. auto const changedPairs =
  58. reconcileStateWithChildren(newChildren, oldChildren);
  59. ShadowNode::SharedListOfShared clonedChildren =
  60. ShadowNodeFragment::childrenPlaceholder();
  61. // If any children were cloned, we need to recreate the child list.
  62. // This won't cause any children to be cloned that weren't already cloned -
  63. // it just collects all children, cloned or uncloned, into a new list.
  64. if (!changedPairs.empty()) {
  65. ShadowNode::UnsharedListOfShared newList =
  66. std::make_shared<ShadowNode::ListOfShared>();
  67. for (std::size_t i = 0, j = 0; i < newChildren.size(); ++i) {
  68. if (j < changedPairs.size() && changedPairs[j].first == newChildren[i]) {
  69. newList->push_back(changedPairs[j].second);
  70. ++j;
  71. } else {
  72. newList->push_back(newChildren[i]);
  73. }
  74. }
  75. clonedChildren = newList;
  76. }
  77. return newNode->clone({/* .props = */ ShadowNodeFragment::propsPlaceholder(),
  78. /* .children = */ clonedChildren,
  79. /* .state = */ newNode->getMostRecentState()});
  80. }
  81. } // namespace react
  82. } // namespace facebook