-
Notifications
You must be signed in to change notification settings - Fork 24.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fabric:
Element<>
, a declarative way to describe a component hierarchy
Summary: `Element` is an abstraction layer that allows describing component hierarchy in a declarative way. Creating `Element`s themself does not create a component tree (aka `ShadowNode` tree) but describes some hierarchical structure that might be used to build an actual component tree (similar to XML Elements). `Element<>` provides some basic type-safety guarantees: all modifications of element objects require using objects (such as Props or State) of compatible type. For now, the only useful application of that is building tests. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D19512392 fbshipit-source-id: eb0711c2a537865fa5454dbede53412a135058cf
- Loading branch information
1 parent
383934a
commit 6a1438c
Showing
8 changed files
with
511 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_debug_preprocessor_flags") | ||
load( | ||
"//tools/build_defs/oss:rn_defs.bzl", | ||
"ANDROID", | ||
"APPLE", | ||
"CXX", | ||
"fb_xplat_cxx_test", | ||
"get_apple_compiler_flags", | ||
"get_apple_inspector_flags", | ||
"react_native_xplat_target", | ||
"rn_xplat_cxx_library", | ||
"subdir_glob", | ||
) | ||
|
||
APPLE_COMPILER_FLAGS = get_apple_compiler_flags() | ||
|
||
rn_xplat_cxx_library( | ||
name = "element", | ||
srcs = glob( | ||
["**/*.cpp"], | ||
exclude = glob(["tests/**/*.cpp"]), | ||
), | ||
headers = glob( | ||
["**/*.h"], | ||
exclude = glob(["tests/**/*.h"]), | ||
), | ||
header_namespace = "", | ||
exported_headers = subdir_glob( | ||
[ | ||
("", "*.h"), | ||
], | ||
prefix = "react/element", | ||
), | ||
compiler_flags = [ | ||
"-fexceptions", | ||
"-frtti", | ||
"-std=c++14", | ||
"-Wall", | ||
], | ||
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, | ||
fbobjc_labels = ["supermodule:ios/isolation/infra.react_native"], | ||
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + get_apple_inspector_flags(), | ||
force_static = True, | ||
macosx_tests_override = [], | ||
platforms = (ANDROID, APPLE, CXX), | ||
preprocessor_flags = [ | ||
"-DLOG_TAG=\"ReactNative\"", | ||
# Systraces are temporary disabled. | ||
# "-DWITH_FBSYSTRACE=1", | ||
], | ||
tests = [":tests"], | ||
visibility = ["PUBLIC"], | ||
deps = [ | ||
"fbsource//xplat/fbsystrace:fbsystrace", | ||
react_native_xplat_target("fabric/components/view:view"), | ||
react_native_xplat_target("fabric/uimanager:uimanager"), | ||
react_native_xplat_target("fabric/core:core"), | ||
react_native_xplat_target("fabric/debug:debug"), | ||
react_native_xplat_target("utils:utils"), | ||
], | ||
) | ||
|
||
fb_xplat_cxx_test( | ||
name = "tests", | ||
srcs = glob(["tests/**/*.cpp"]), | ||
headers = glob(["tests/**/*.h"]), | ||
compiler_flags = [ | ||
"-fexceptions", | ||
"-frtti", | ||
"-std=c++14", | ||
"-Wall", | ||
], | ||
contacts = ["oncall+react_native@xmail.facebook.com"], | ||
platforms = (ANDROID, APPLE, CXX), | ||
deps = [ | ||
"fbsource//xplat/third-party/gmock:gtest", | ||
":element", | ||
react_native_xplat_target("fabric/components/root:root"), | ||
react_native_xplat_target("fabric/components/view:view"), | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* 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 "ComponentBuilder.h" | ||
|
||
namespace facebook { | ||
namespace react { | ||
|
||
ComponentBuilder::ComponentBuilder( | ||
ComponentDescriptorRegistry::Shared const &componentDescriptorRegistry) | ||
: componentDescriptorRegistry_(componentDescriptorRegistry){}; | ||
|
||
ShadowNode::Shared ComponentBuilder::build( | ||
ElementFragment const &elementFragment) const { | ||
auto &componentDescriptor = | ||
componentDescriptorRegistry_->at(elementFragment.componentHandle); | ||
|
||
auto children = ShadowNode::ListOfShared{}; | ||
children.reserve(elementFragment.children.size()); | ||
for (auto const &childFragment : elementFragment.children) { | ||
children.push_back(build(childFragment)); | ||
} | ||
|
||
auto eventEmitter = | ||
componentDescriptor.createEventEmitter(nullptr, elementFragment.tag); | ||
|
||
auto shadowNode = componentDescriptor.createShadowNode( | ||
ShadowNodeFragment{ | ||
elementFragment.props, | ||
std::make_shared<ShadowNode::ListOfShared const>(children), | ||
elementFragment.state}, | ||
ShadowNodeFamilyFragment{ | ||
elementFragment.tag, elementFragment.surfaceId, eventEmitter}); | ||
|
||
if (elementFragment.referenceCallback) { | ||
elementFragment.referenceCallback(shadowNode); | ||
} | ||
|
||
if (elementFragment.finalizeCallback) { | ||
elementFragment.finalizeCallback(const_cast<ShadowNode &>(*shadowNode)); | ||
} | ||
|
||
return shadowNode; | ||
} | ||
|
||
} // namespace react | ||
} // namespace facebook |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <memory> | ||
|
||
#include <react/core/ComponentDescriptor.h> | ||
#include <react/core/ShadowNode.h> | ||
#include <react/core/ShadowNodeFamilyFragment.h> | ||
#include <react/core/ShadowNodeFragment.h> | ||
#include <react/uimanager/ComponentDescriptorRegistry.h> | ||
|
||
#include <react/element/Element.h> | ||
#include <react/element/ElementFragment.h> | ||
|
||
namespace facebook { | ||
namespace react { | ||
|
||
/* | ||
* Build `ShadowNode` trees with a given given `Element` trees. | ||
*/ | ||
class ComponentBuilder final { | ||
public: | ||
ComponentBuilder( | ||
ComponentDescriptorRegistry::Shared const &componentDescriptorRegistry); | ||
|
||
/* | ||
* Builds a `ShadowNode` tree with given `Element` tree using stored | ||
* `ComponentDescriptorRegistry`. | ||
*/ | ||
template <typename ShadowNodeT> | ||
std::shared_ptr<ShadowNodeT const> build(Element<ShadowNodeT> element) const { | ||
return std::static_pointer_cast<ShadowNodeT const>( | ||
build(element.fragment_)); | ||
} | ||
|
||
private: | ||
/* | ||
* Internal, type-erased version of `build`. | ||
*/ | ||
ShadowNode::Shared build(ElementFragment const &elementFragment) const; | ||
|
||
ComponentDescriptorRegistry::Shared componentDescriptorRegistry_; | ||
}; | ||
|
||
} // namespace react | ||
} // namespace facebook |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* | ||
* 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 "Element.h" | ||
|
||
// Intentionally empty. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <functional> | ||
#include <memory> | ||
|
||
#include <react/core/ShadowNode.h> | ||
|
||
#include <react/element/ElementFragment.h> | ||
|
||
namespace facebook { | ||
namespace react { | ||
|
||
/* | ||
* `Element<>` is an abstraction layer that allows describing component | ||
* hierarchy in a declarative way. Creating `Element`s themself does not create | ||
* a component tree (aka `ShadowNode` tree) but describes some hierarchical | ||
* structure that might be used to build an actual component tree (similar to | ||
* XML Elements). | ||
* `Element` provides some basic type-safety guarantees: all modifications | ||
* of element objects require using objects (such as Props or State) of | ||
* compatible type. | ||
*/ | ||
template <typename ShadowNodeT> | ||
class Element final { | ||
public: | ||
using ConcreteProps = typename ShadowNodeT::ConcreteProps; | ||
using SharedConcreteProps = typename ShadowNodeT::SharedConcreteProps; | ||
using ConcreteEventEmitter = typename ShadowNodeT::ConcreteEventEmitter; | ||
using ConcreteShadowNode = ShadowNodeT; | ||
using ConcreteSharedShadowNode = std::shared_ptr<ConcreteShadowNode const>; | ||
|
||
using ConcreteReferenceCallback = | ||
std::function<void(std::shared_ptr<ShadowNodeT const> const &shadowNode)>; | ||
|
||
/* | ||
* Constructs an `Element`. | ||
*/ | ||
Element() { | ||
fragment_.componentHandle = ShadowNodeT::Handle(); | ||
fragment_.componentName = ShadowNodeT::Name(); | ||
fragment_.props = ShadowNodeT::defaultSharedProps(); | ||
} | ||
|
||
/* | ||
* Sets `tag`. | ||
*/ | ||
Element &tag(Tag tag) { | ||
fragment_.tag = tag; | ||
return *this; | ||
} | ||
|
||
/* | ||
* Sets `surfaceId`. | ||
*/ | ||
Element &surfaceId(SurfaceId surfaceId) { | ||
fragment_.surfaceId = surfaceId; | ||
return *this; | ||
} | ||
|
||
/* | ||
* Sets `props`. | ||
*/ | ||
Element &props(SharedConcreteProps props) { | ||
fragment_.props = props; | ||
return *this; | ||
} | ||
|
||
/* | ||
* Sets `props` using callback. | ||
*/ | ||
Element &props(std::function<SharedConcreteProps()> callback) { | ||
fragment_.props = callback(); | ||
return *this; | ||
} | ||
|
||
/* | ||
* Sets children. | ||
*/ | ||
Element &children(std::vector<Element> children) { | ||
auto fragments = ElementFragment::List{}; | ||
fragments.reserve(children.size()); | ||
for (auto const &child : children) { | ||
fragments.push_back(child.fragment_); | ||
} | ||
fragment_.children = fragments; | ||
return *this; | ||
} | ||
|
||
/* | ||
* Calls the callback during component construction with a pointer to the | ||
* component which is being constructed. | ||
*/ | ||
Element &reference( | ||
std::function<void(ConcreteSharedShadowNode const &shadowNode)> | ||
callback) { | ||
fragment_.referenceCallback = callback; | ||
return *this; | ||
} | ||
|
||
/* | ||
* During component construction, assigns a given pointer to a component | ||
* that is being constructed. | ||
*/ | ||
Element &reference(ConcreteSharedShadowNode &inShadowNode) { | ||
fragment_.referenceCallback = [&](ShadowNode::Shared const &shadowNode) { | ||
inShadowNode = | ||
std::static_pointer_cast<ConcreteShadowNode const>(shadowNode); | ||
}; | ||
return *this; | ||
} | ||
|
||
/* | ||
* Calls the callback with a reference to a just constructed component. | ||
*/ | ||
Element &finalize( | ||
std::function<void(ConcreteShadowNode &shadowNode)> finalizeCallback) { | ||
fragment_.finalizeCallback = [=](ShadowNode &shadowNode) { | ||
return finalizeCallback(static_cast<ConcreteShadowNode &>(shadowNode)); | ||
}; | ||
return *this; | ||
} | ||
|
||
private: | ||
friend class ComponentBuilder; | ||
ElementFragment fragment_; | ||
}; | ||
|
||
} // namespace react | ||
} // namespace facebook |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* | ||
* 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 "ElementFragment.h" | ||
|
||
// Intentionally empty. |
Oops, something went wrong.