Fabric/Text: ShadowNode::clone() - dynamic self-cloning mechanism
Summary: The previous approach simply didn't work. :( As you might notice, in previous implementation of ViewShadowNode::cloneAndReplaceChild we used `ViewShadowNode` class for cloning `childNode`, and this is incorrect becasue `childNode` might be instance of any class. Reviewed By: fkgozali Differential Revision: D7595016 fbshipit-source-id: 2215414926f2f7a2e2fd05ca2d065f10d6d32b74
This commit is contained in:
parent
2e7dbc82b3
commit
bd91eaf664
|
@ -8,6 +8,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#include <fabric/core/ComponentDescriptor.h>
|
||||
#include <fabric/core/Props.h>
|
||||
|
@ -40,7 +41,14 @@ public:
|
|||
const InstanceHandle &instanceHandle,
|
||||
const SharedProps &props
|
||||
) const override {
|
||||
return std::make_shared<ShadowNodeT>(tag, rootTag, instanceHandle, std::static_pointer_cast<const ConcreteProps>(props));
|
||||
return std::make_shared<ShadowNodeT>(
|
||||
tag,
|
||||
rootTag,
|
||||
instanceHandle,
|
||||
std::static_pointer_cast<const ConcreteProps>(props),
|
||||
ShadowNode::emptySharedShadowNodeSharedList(),
|
||||
getCloneFunction()
|
||||
);
|
||||
}
|
||||
|
||||
SharedShadowNode cloneShadowNode(
|
||||
|
@ -67,6 +75,18 @@ public:
|
|||
return ShadowNodeT::Props(rawProps, props);
|
||||
};
|
||||
|
||||
private:
|
||||
mutable ShadowNodeCloneFunction cloneFunction_;
|
||||
|
||||
ShadowNodeCloneFunction getCloneFunction() const {
|
||||
if (!cloneFunction_) {
|
||||
cloneFunction_ = [this](const SharedShadowNode &shadowNode, const SharedProps &props, const SharedShadowNodeSharedList &children) {
|
||||
return this->cloneShadowNode(shadowNode, props, children);
|
||||
};
|
||||
}
|
||||
|
||||
return cloneFunction_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
|
|
|
@ -51,14 +51,16 @@ public:
|
|||
const Tag &rootTag,
|
||||
const InstanceHandle &instanceHandle,
|
||||
const SharedConcreteProps &props = ConcreteShadowNode::defaultSharedProps(),
|
||||
const SharedShadowNodeSharedList &children = ShadowNode::emptySharedShadowNodeSharedList()
|
||||
const SharedShadowNodeSharedList &children = ShadowNode::emptySharedShadowNodeSharedList(),
|
||||
const ShadowNodeCloneFunction &cloneFunction = nullptr
|
||||
):
|
||||
ShadowNode(
|
||||
tag,
|
||||
rootTag,
|
||||
instanceHandle,
|
||||
(SharedProps)props,
|
||||
children
|
||||
children,
|
||||
cloneFunction
|
||||
) {};
|
||||
|
||||
ConcreteShadowNode(
|
||||
|
|
|
@ -25,13 +25,15 @@ ShadowNode::ShadowNode(
|
|||
const Tag &rootTag,
|
||||
const InstanceHandle &instanceHandle,
|
||||
const SharedProps &props,
|
||||
const SharedShadowNodeSharedList &children
|
||||
const SharedShadowNodeSharedList &children,
|
||||
const ShadowNodeCloneFunction &cloneFunction
|
||||
):
|
||||
tag_(tag),
|
||||
rootTag_(rootTag),
|
||||
instanceHandle_(instanceHandle),
|
||||
props_(props),
|
||||
children_(std::make_shared<SharedShadowNodeList>(*children)),
|
||||
cloneFunction_(cloneFunction),
|
||||
revision_(1) {}
|
||||
|
||||
ShadowNode::ShadowNode(
|
||||
|
@ -45,8 +47,17 @@ ShadowNode::ShadowNode(
|
|||
props_(props ? props : shadowNode->props_),
|
||||
children_(std::make_shared<SharedShadowNodeList>(*(children ? children : shadowNode->children_))),
|
||||
sourceNode_(shadowNode),
|
||||
cloneFunction_(shadowNode->cloneFunction_),
|
||||
revision_(shadowNode->revision_ + 1) {}
|
||||
|
||||
SharedShadowNode ShadowNode::clone(
|
||||
const SharedProps &props,
|
||||
const SharedShadowNodeSharedList &children
|
||||
) const {
|
||||
assert(cloneFunction_);
|
||||
return cloneFunction_(shared_from_this(), props_, children_);
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
SharedShadowNodeSharedList ShadowNode::getChildren() const {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <fabric/core/Props.h>
|
||||
|
@ -26,9 +27,12 @@ using SharedShadowNodeSharedList = std::shared_ptr<const SharedShadowNodeList>;
|
|||
using SharedShadowNodeUnsharedList = std::shared_ptr<SharedShadowNodeList>;
|
||||
using WeakShadowNode = std::weak_ptr<const ShadowNode>;
|
||||
|
||||
using ShadowNodeCloneFunction = std::function<SharedShadowNode(SharedShadowNode shadowNode, SharedProps props, SharedShadowNodeSharedList children)>;
|
||||
|
||||
class ShadowNode:
|
||||
public virtual Sealable,
|
||||
public virtual DebugStringConvertible {
|
||||
public virtual DebugStringConvertible,
|
||||
public std::enable_shared_from_this<ShadowNode> {
|
||||
public:
|
||||
static SharedShadowNodeSharedList emptySharedShadowNodeSharedList();
|
||||
|
||||
|
@ -39,7 +43,8 @@ public:
|
|||
const Tag &rootTag,
|
||||
const InstanceHandle &instanceHandle,
|
||||
const SharedProps &props = SharedProps(),
|
||||
const SharedShadowNodeSharedList &children = SharedShadowNodeSharedList()
|
||||
const SharedShadowNodeSharedList &children = SharedShadowNodeSharedList(),
|
||||
const ShadowNodeCloneFunction &cloneFunction = nullptr
|
||||
);
|
||||
|
||||
ShadowNode(
|
||||
|
@ -48,6 +53,14 @@ public:
|
|||
const SharedShadowNodeSharedList &children = nullptr
|
||||
);
|
||||
|
||||
/*
|
||||
* Clones the shadow node using stored `cloneFunction`.
|
||||
*/
|
||||
SharedShadowNode clone(
|
||||
const SharedProps &props = nullptr,
|
||||
const SharedShadowNodeSharedList &children = nullptr
|
||||
) const;
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
virtual ComponentHandle getComponentHandle() const = 0;
|
||||
|
@ -112,6 +125,12 @@ protected:
|
|||
|
||||
private:
|
||||
|
||||
/*
|
||||
* A reference to a cloning function that understands how to clone
|
||||
* the specific type of ShadowNode.
|
||||
*/
|
||||
ShadowNodeCloneFunction cloneFunction_;
|
||||
|
||||
/*
|
||||
* A number of the generation of the ShadowNode instance;
|
||||
* is used and useful for debug-printing purposes *only*.
|
||||
|
|
|
@ -115,3 +115,38 @@ TEST(ShadowNodeTest, handleSourceNode) {
|
|||
ASSERT_EQ(nodeThirdGeneration->getSourceNode(), nodeSecondGeneration);
|
||||
ASSERT_EQ(nodeSecondGeneration->getSourceNode(), nodeFirstGeneration);
|
||||
}
|
||||
|
||||
TEST(ShadowNodeTest, handleCloneFunction) {
|
||||
auto firstNode = std::make_shared<TestShadowNode>(9, 1, (void *)NULL);
|
||||
|
||||
// The shadow node is not clonable if `cloneFunction` is not provided,
|
||||
ASSERT_DEATH_IF_SUPPORTED(firstNode->clone(), "cloneFunction_");
|
||||
|
||||
auto secondNode = std::make_shared<TestShadowNode>(
|
||||
9,
|
||||
1,
|
||||
(void *)NULL,
|
||||
std::make_shared<const TestProps>(),
|
||||
ShadowNode::emptySharedShadowNodeSharedList(),
|
||||
[](const SharedShadowNode &shadowNode, const SharedProps &props, const SharedShadowNodeSharedList &children) {
|
||||
return std::make_shared<const TestShadowNode>(
|
||||
std::static_pointer_cast<const TestShadowNode>(shadowNode),
|
||||
props,
|
||||
children
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
auto secondNodeClone = secondNode->clone();
|
||||
|
||||
// Those two nodes are *not* same.
|
||||
ASSERT_NE(secondNode, secondNodeClone);
|
||||
|
||||
// `secondNodeClone` is an instance of `TestShadowNode`.
|
||||
ASSERT_NE(std::dynamic_pointer_cast<const TestShadowNode>(secondNodeClone), nullptr);
|
||||
|
||||
// Both nodes have same content.
|
||||
ASSERT_EQ(secondNode->getTag(), secondNodeClone->getTag());
|
||||
ASSERT_EQ(secondNode->getRootTag(), secondNodeClone->getRootTag());
|
||||
ASSERT_EQ(secondNode->getProps(), secondNodeClone->getProps());
|
||||
}
|
||||
|
|
|
@ -21,14 +21,16 @@ ViewShadowNode::ViewShadowNode(
|
|||
const Tag &rootTag,
|
||||
const InstanceHandle &instanceHandle,
|
||||
const SharedViewProps &props,
|
||||
const SharedShadowNodeSharedList &children
|
||||
const SharedShadowNodeSharedList &children,
|
||||
const ShadowNodeCloneFunction &cloneFunction
|
||||
):
|
||||
ConcreteShadowNode(
|
||||
tag,
|
||||
rootTag,
|
||||
instanceHandle,
|
||||
props,
|
||||
children
|
||||
children,
|
||||
cloneFunction
|
||||
),
|
||||
AccessibleShadowNode(
|
||||
props
|
||||
|
@ -92,9 +94,9 @@ SharedLayoutableShadowNodeList ViewShadowNode::getLayoutableChildNodes() const {
|
|||
SharedLayoutableShadowNode ViewShadowNode::cloneAndReplaceChild(const SharedLayoutableShadowNode &child) {
|
||||
ensureUnsealed();
|
||||
|
||||
auto viewShadowNodeChild = std::dynamic_pointer_cast<const ViewShadowNode>(child);
|
||||
assert(viewShadowNodeChild);
|
||||
auto viewShadowNodeChildClone = std::make_shared<ViewShadowNode>(viewShadowNodeChild);
|
||||
auto childShadowNode = std::dynamic_pointer_cast<const ShadowNode>(child);
|
||||
assert(childShadowNode);
|
||||
auto childShadowNodeClone = childShadowNode->clone();
|
||||
|
||||
// This is overloading of `SharedLayoutableShadowNode::cloneAndReplaceChild`,
|
||||
// the method is used to clone some node as a preparation for future mutation
|
||||
|
@ -107,10 +109,11 @@ SharedLayoutableShadowNode ViewShadowNode::cloneAndReplaceChild(const SharedLayo
|
|||
// In other words, if we don't compensate this change here,
|
||||
// the Diffing algorithm will compare wrong trees
|
||||
// ("new-but-not-laid-out-yet vs. new" instead of "committed vs. new").
|
||||
viewShadowNodeChildClone->shallowSourceNode();
|
||||
auto nonConstChildShadowNodeClone = std::const_pointer_cast<ShadowNode>(childShadowNodeClone);
|
||||
nonConstChildShadowNodeClone->shallowSourceNode();
|
||||
|
||||
ShadowNode::replaceChild(viewShadowNodeChild, viewShadowNodeChildClone);
|
||||
return std::static_pointer_cast<const LayoutableShadowNode>(viewShadowNodeChildClone);
|
||||
ShadowNode::replaceChild(childShadowNode, childShadowNodeClone);
|
||||
return std::dynamic_pointer_cast<const LayoutableShadowNode>(childShadowNodeClone);
|
||||
}
|
||||
|
||||
#pragma mark - Equality
|
||||
|
|
|
@ -35,7 +35,8 @@ public:
|
|||
const Tag &rootTag,
|
||||
const InstanceHandle &instanceHandle,
|
||||
const SharedViewProps &props = ViewShadowNode::defaultSharedProps(),
|
||||
const SharedShadowNodeSharedList &children = ShadowNode::emptySharedShadowNodeSharedList()
|
||||
const SharedShadowNodeSharedList &children = ShadowNode::emptySharedShadowNodeSharedList(),
|
||||
const ShadowNodeCloneFunction &cloneFunction = nullptr
|
||||
);
|
||||
|
||||
ViewShadowNode(
|
||||
|
|
Loading…
Reference in New Issue