Fabric: Introducing ComponentDescriptor::Flavor

Summary:
Currently, the same ComponentDescriptor class cannot be registered as a responder for components with different names. However, we have marginal cases where we really need it. The examples are `UnimplementedView` or possible universal interop with the classic RN or any other UI framework.

This change adds a special optional argument to ComponentDescript constructor that allows implementing this functionality.

Reviewed By: fkgozali

Differential Revision: D17211915

fbshipit-source-id: 18f59e09fe06b875a8e8975b7b2ab423489238bb
This commit is contained in:
Valentin Shergin
2019-09-29 20:03:41 -07:00
committed by Facebook Github Bot
parent df229590b2
commit c5cc27f1e9
8 changed files with 51 additions and 20 deletions
@@ -23,8 +23,9 @@ class ImageComponentDescriptor final
public:
ImageComponentDescriptor(
EventDispatcher::Weak eventDispatcher,
ContextContainer::Shared const &contextContainer)
: ConcreteComponentDescriptor(eventDispatcher),
ContextContainer::Shared const &contextContainer,
ComponentDescriptor::Flavor const &flavor = {})
: ConcreteComponentDescriptor(eventDispatcher, contextContainer, flavor),
imageManager_(std::make_shared<ImageManager>(contextContainer)){};
void adopt(UnsharedShadowNode shadowNode) const override {
@@ -22,8 +22,9 @@ class SliderComponentDescriptor final
public:
SliderComponentDescriptor(
EventDispatcher::Weak eventDispatcher,
ContextContainer::Shared const &contextContainer)
: ConcreteComponentDescriptor(eventDispatcher),
ContextContainer::Shared const &contextContainer,
ComponentDescriptor::Flavor const &flavor = {})
: ConcreteComponentDescriptor(eventDispatcher, contextContainer, flavor),
imageManager_(std::make_shared<ImageManager>(contextContainer)),
measurementsManager_(
SliderMeasurementsManager::shouldMeasureSlider()
@@ -27,8 +27,12 @@ class ParagraphComponentDescriptor final
public:
ParagraphComponentDescriptor(
EventDispatcher::Weak eventDispatcher,
ContextContainer::Shared const &contextContainer)
: ConcreteComponentDescriptor<ParagraphShadowNode>(eventDispatcher) {
ContextContainer::Shared const &contextContainer,
ComponentDescriptor::Flavor const &flavor = {})
: ConcreteComponentDescriptor<ParagraphShadowNode>(
eventDispatcher,
contextContainer,
flavor) {
// Every single `ParagraphShadowNode` will have a reference to
// a shared `TextLayoutManager`.
textLayoutManager_ = std::make_shared<TextLayoutManager>(contextContainer);
@@ -12,8 +12,11 @@ namespace react {
ComponentDescriptor::ComponentDescriptor(
EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer)
: eventDispatcher_(eventDispatcher), contextContainer_(contextContainer) {}
ContextContainer::Shared const &contextContainer,
ComponentDescriptor::Flavor const &flavor)
: eventDispatcher_(eventDispatcher),
contextContainer_(contextContainer),
flavor_(flavor) {}
ContextContainer::Shared const &ComponentDescriptor::getContextContainer()
const {
@@ -33,9 +33,21 @@ class ComponentDescriptor {
using Shared = std::shared_ptr<ComponentDescriptor const>;
using Unique = std::unique_ptr<ComponentDescriptor const>;
/*
* `Flavor` is a special concept designed to allow registering instances of
* the exact same `ComponentDescriptor` class with different `ComponentName`
* and `ComponentHandle` (the particular custom implementation might use
* stored `flavor` to return different values from those virtual methods).
* Since it's a very niche requirement (e.g. we plan to use it for
* an interoperability layer with Paper), we are thinking about removing this
* feature completely after it's no longer needed.
*/
using Flavor = std::shared_ptr<void const>;
ComponentDescriptor(
EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer);
ContextContainer::Shared const &contextContainer,
ComponentDescriptor::Flavor const &flavor);
virtual ~ComponentDescriptor() = default;
@@ -114,6 +126,7 @@ class ComponentDescriptor {
EventDispatcher::Weak eventDispatcher_;
ContextContainer::Shared contextContainer_;
RawPropsParser rawPropsParser_{};
Flavor flavor_;
};
} // namespace react
@@ -47,8 +47,9 @@ class ConcreteComponentDescriptor : public ComponentDescriptor {
ConcreteComponentDescriptor(
EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer = {})
: ComponentDescriptor(eventDispatcher, contextContainer) {
ContextContainer::Shared const &contextContainer = {},
ComponentDescriptor::Flavor const &flavor = {})
: ComponentDescriptor(eventDispatcher, contextContainer, flavor) {
rawPropsParser_.prepare<ConcreteProps>();
}
@@ -22,6 +22,7 @@ class ComponentDescriptorParameters {
public:
EventDispatcher::Weak eventDispatcher;
ContextContainer::Shared contextContainer;
ComponentDescriptor::Flavor flavor;
};
/*
@@ -30,36 +31,40 @@ class ComponentDescriptorParameters {
* abstract type and ownership of the newly created object.
*/
using ComponentDescriptorConstructor = ComponentDescriptor::Unique(
EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer);
ComponentDescriptorParameters const &parameters);
/*
* Represents a unified way to construct an instance of a particular stored
* `ComponentDescriptor` class. C++ does not allow to create pointers to
* constructors, so we have to have such data structure to manipulate a
* collection of classes.
*
* Note: The actual values of `handle` and `name` for some components depend on
* `flavor`. The provider is valid if instantiated by `constructor` object with
* given `flavor` exposes the same values of `handle` and `name`.
*/
class ComponentDescriptorProvider final {
public:
ComponentHandle handle;
ComponentName name;
ComponentDescriptor::Flavor flavor;
ComponentDescriptorConstructor *constructor;
};
/*
* Creates a `ComponentDescriptorConstructor` for given `ComponentDescriptor`
* class.
* Creates a `ComponentDescriptor` for given `ComponentDescriptorParameters`.
*/
template <typename ComponentDescriptorT>
ComponentDescriptor::Unique concreteComponentDescriptorConstructor(
EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer) {
ComponentDescriptorParameters const &parameters) {
static_assert(
std::is_base_of<ComponentDescriptor, ComponentDescriptorT>::value,
"ComponentDescriptorT must be a descendant of ComponentDescriptor");
return std::make_unique<ComponentDescriptorT const>(
eventDispatcher, contextContainer);
parameters.eventDispatcher,
parameters.contextContainer,
parameters.flavor);
}
/*
@@ -74,7 +79,8 @@ ComponentDescriptorProvider concreteComponentDescriptorProvider() {
return {ComponentDescriptorT::ConcreteShadowNode::Handle(),
ComponentDescriptorT::ConcreteShadowNode::Name(),
&concreteComponentDescriptorConstructor<ComponentDescriptorT>};
&concreteComponentDescriptorConstructor<ComponentDescriptorT>,
nullptr};
}
} // namespace react
@@ -20,7 +20,9 @@ void ComponentDescriptorRegistry::add(
std::unique_lock<better::shared_mutex> lock(mutex_);
auto componentDescriptor = componentDescriptorProvider.constructor(
parameters_.eventDispatcher, parameters_.contextContainer);
{parameters_.eventDispatcher,
parameters_.contextContainer,
componentDescriptorProvider.flavor});
assert(
componentDescriptor->getComponentHandle() ==
componentDescriptorProvider.handle);