Revert D23292369: Remove State Reconciliation flags and dead code

Differential Revision:
D23292369 (https://github.com/facebook/react-native/commit/3cb78d5189665eaa07f4b407efdf06564cd45fd4)

Original commit changeset: 6f00070495e9

fbshipit-source-id: 7e41f6fbdbdb1b833b16c5f3f5ece35a5d7adebe
This commit is contained in:
Stephy Ma
2020-08-24 19:04:09 -07:00
committed by Facebook GitHub Bot
parent 6d58490187
commit 03d69fce59
6 changed files with 158 additions and 5 deletions
@@ -17,6 +17,7 @@
#include <react/renderer/mounting/ShadowViewMutation.h>
#include "ShadowTreeDelegate.h"
#include "TreeStateReconciliation.h"
namespace facebook {
namespace react {
@@ -302,11 +303,22 @@ bool ShadowTree::tryCommit(
}
if (enableStateReconciliation) {
auto updatedNewRootShadowNode =
progressState(*newRootShadowNode, *oldRootShadowNode);
if (updatedNewRootShadowNode) {
newRootShadowNode =
std::static_pointer_cast<RootShadowNode>(updatedNewRootShadowNode);
if (enableNewStateReconciliation_) {
auto updatedNewRootShadowNode =
progressState(*newRootShadowNode, *oldRootShadowNode);
if (updatedNewRootShadowNode) {
newRootShadowNode =
std::static_pointer_cast<RootShadowNode>(updatedNewRootShadowNode);
}
} else {
// Compare state revisions of old and new root
// Children of the root node may be mutated in-place
UnsharedShadowNode reconciledNode =
reconcileStateWithTree(newRootShadowNode.get(), oldRootShadowNode);
if (reconciledNode != nullptr) {
newRootShadowNode = std::make_shared<RootShadowNode>(
*reconciledNode, ShadowNodeFragment{});
}
}
}
@@ -81,6 +81,13 @@ class ShadowTree final {
MountingCoordinator::Shared getMountingCoordinator() const;
/*
* Temporary.
* Do not use.
*/
void setEnableNewStateReconciliation(bool value) {
enableNewStateReconciliation_ = value;
}
void setEnableReparentingDetection(bool value) {
enableReparentingDetection_ = value;
}
@@ -0,0 +1,94 @@
/*
* 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, ShadowNode::Unshared>>;
/**
* 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
@@ -0,0 +1,33 @@
/*
* 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 <react/renderer/core/ShadowNode.h>
#include <react/renderer/core/ShadowNodeFragment.h>
namespace facebook {
namespace react {
/**
* Problem Description: because of C++ State, the React Native C++ ShadowTree
* can diverge from the ReactJS ShadowTree; ReactJS communicates all tree
* changes to C++, but C++ state commits are not propagated to ReactJS (ReactJS
* may or may not clone nodes with state changes, but it has no way of knowing
* if it /should/ clone those nodes; so those clones may never happen). This
* causes a number of problems. This function resolves the problem by taking a
* candidate tree being committed, and sees if any State changes need to be
* applied to it. If any changes need to be made, a new ShadowNode is returned;
* otherwise, nullptr is returned if the node is already consistent with the
* latest tree, including all state changes.
*
* This should be called during the commit phase, pre-layout and pre-diff.
*/
UnsharedShadowNode reconcileStateWithTree(
ShadowNode const *newNode,
SharedShadowNode committedNode);
} // namespace react
} // namespace facebook
@@ -108,11 +108,15 @@ Scheduler::Scheduler(
#ifdef ANDROID
enableReparentingDetection_ = reactNativeConfig_->getBool(
"react_fabric:enable_reparenting_detection_android");
enableNewStateReconciliation_ = reactNativeConfig_->getBool(
"react_fabric:enable_new_state_reconciliation_android");
removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool(
"react_fabric:remove_outstanding_surfaces_on_destruction_android");
#else
enableReparentingDetection_ = reactNativeConfig_->getBool(
"react_fabric:enable_reparenting_detection_ios");
enableNewStateReconciliation_ = reactNativeConfig_->getBool(
"react_fabric:enable_new_state_reconciliation_ios");
removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool(
"react_fabric:remove_outstanding_surfaces_on_destruction_ios");
#endif
@@ -188,6 +192,8 @@ void Scheduler::startSurface(
mountingOverrideDelegate,
enableReparentingDetection_);
shadowTree->setEnableNewStateReconciliation(enableNewStateReconciliation_);
auto uiManager = uiManager_;
uiManager->getShadowTreeRegistry().add(std::move(shadowTree));
@@ -138,6 +138,7 @@ class Scheduler final : public UIManagerDelegate {
* Temporary flags.
*/
bool enableReparentingDetection_{false};
bool enableNewStateReconciliation_{false};
bool removeOutstandingSurfacesOnDestruction_{false};
};