Skip to content

Commit

Permalink
Add setNativeProps to Fabric
Browse files Browse the repository at this point in the history
Summary:
changelog: Introduce setNativeProps to Fabric

Add support for `setNativeProps` in Fabric for backwards compatibility. It is still recommended to move away from `setNativeProps` because the API will not work with future features.

We can make step [Migrating off setNativeProps](https://reactnative.dev/docs/new-architecture-library-intro#migrating-off-setnativeprops) in migration guide optional.

Reviewed By: yungsters, mdvacca

Differential Revision: D41521523

fbshipit-source-id: 4d9bbd6304b8c5ee24a36b33039ed33ae1fc21f8
  • Loading branch information
sammy-SC authored and facebook-github-bot committed Dec 6, 2022
1 parent 86786f1 commit 1d3fa40
Show file tree
Hide file tree
Showing 18 changed files with 165 additions and 15 deletions.
8 changes: 8 additions & 0 deletions React/Fabric/Mounting/RCTMountingManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import <React/RCTMountingManagerDelegate.h>
#import <React/RCTPrimitives.h>
#import <react/renderer/core/ComponentDescriptor.h>
#import <react/renderer/core/RawProps.h>
#import <react/renderer/core/ReactPrimitives.h>
#import <react/renderer/mounting/MountingCoordinator.h>
#import <react/renderer/mounting/ShadowView.h>
Expand Down Expand Up @@ -55,6 +56,13 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)dispatchCommand:(ReactTag)reactTag commandName:(NSString *)commandName args:(NSArray *)args;

/**
* Set props on native view directly. It is a performance shortcut that skips render pipeline.
* This is a backport of setNativeProps from the old architecture and will be removed in the future.
* Can be called from any thread.
*/
- (void)setNativeProps_DEPRECATED:(ReactTag)reactTag withProps:(facebook::react::Props::Shared)props;

/**
* Dispatch an accessibility event to be performed on the main thread.
* Can be called from any thread.
Expand Down
16 changes: 14 additions & 2 deletions React/Fabric/Mounting/RCTMountingManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,24 @@ - (void)dispatchCommand:(ReactTag)reactTag commandName:(NSString *)commandName a
}

RCTExecuteOnMainQueue(^{
RCTAssertMainQueue();
[self synchronouslyDispatchCommandOnUIThread:reactTag commandName:commandName args:args];
});
}

- (void)setNativeProps_DEPRECATED:(ReactTag)reactTag withProps:(Props::Shared)props
{
if (RCTIsMainQueue()) {
UIView<RCTComponentViewProtocol> *componentView = [_componentViewRegistry findComponentViewWithTag:reactTag];
Props::Shared oldProps = [componentView props];
[componentView updateProps:props oldProps:oldProps];
[componentView finalizeUpdates:RNComponentViewUpdateMaskProps];
return;
}
RCTExecuteOnMainQueue(^{
[self setNativeProps_DEPRECATED:reactTag withProps:std::move(props)];
});
}

- (void)sendAccessibilityEvent:(ReactTag)reactTag eventType:(NSString *)eventType
{
if (RCTIsMainQueue()) {
Expand All @@ -241,7 +254,6 @@ - (void)sendAccessibilityEvent:(ReactTag)reactTag eventType:(NSString *)eventTyp
}

RCTExecuteOnMainQueue(^{
RCTAssertMainQueue();
[self synchronouslyDispatchAccessbilityEventOnUIThread:reactTag eventType:eventType];
});
}
Expand Down
3 changes: 3 additions & 0 deletions React/Fabric/RCTScheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ NS_ASSUME_NONNULL_BEGIN
commandName:(std::string const &)commandName
args:(folly::dynamic const &)args;

- (void)setNativeProps_DEPRECATED:(facebook::react::ShadowView const &)shadowView
withProps:(facebook::react::Props::Shared)props;

- (void)schedulerDidSendAccessibilityEvent:(facebook::react::ShadowView const &)shadowView
eventType:(std::string const &)eventType;

Expand Down
6 changes: 6 additions & 0 deletions React/Fabric/RCTScheduler.mm
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ void schedulerDidDispatchCommand(
[scheduler.delegate schedulerDidDispatchCommand:shadowView commandName:commandName args:args];
}

void setNativeProps_DEPRECATED(const ShadowView &shadowView, Props::Shared props) override
{
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
[scheduler.delegate setNativeProps_DEPRECATED:shadowView withProps:std::move(props)];
}

void schedulerDidSetIsJSResponder(ShadowView const &shadowView, bool isJSResponder, bool blockNativeResponder)
override
{
Expand Down
6 changes: 6 additions & 0 deletions React/Fabric/RCTSurfacePresenter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,12 @@ - (void)schedulerDidDispatchCommand:(ShadowView const &)shadowView
[_mountingManager dispatchCommand:tag commandName:commandStr args:argsArray];
}

- (void)setNativeProps_DEPRECATED:(ShadowView const &)shadowView withProps:(Props::Shared)props
{
ReactTag tag = shadowView.tag;
[self->_mountingManager setNativeProps_DEPRECATED:tag withProps:props];
}

- (void)schedulerDidSendAccessibilityEvent:(const facebook::react::ShadowView &)shadowView
eventType:(const std::string &)eventType
{
Expand Down
6 changes: 6 additions & 0 deletions ReactAndroid/src/main/jni/react/fabric/Binding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,12 @@ void Binding::schedulerDidDispatchCommand(
mountingManager->dispatchCommand(shadowView, commandName, args);
}

void Binding::setNativeProps_DEPRECATED(
const ShadowView &shadowView,
Props::Shared props) {
// TODO(T130729920): Add Android implementation for setNativeProps.
}

void Binding::schedulerDidSendAccessibilityEvent(
const ShadowView &shadowView,
std::string const &eventType) {
Expand Down
4 changes: 4 additions & 0 deletions ReactAndroid/src/main/jni/react/fabric/Binding.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ class Binding : public jni::HybridClass<Binding>,
std::string const &commandName,
folly::dynamic const &args) override;

void setNativeProps_DEPRECATED(
const ShadowView &shadowView,
Props::Shared props) override;

void schedulerDidSendAccessibilityEvent(
const ShadowView &shadowView,
std::string const &eventType) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ void YogaLayoutableShadowNode::adoptYogaChild(size_t index) {
// react_native_assert(layoutableChildNode.yogaNode_.isDirty());
} else {
// The child is owned by some other node, we need to clone that.
// TODO: At this point, React has wrong reference to the node. (T138668036)
auto clonedChildNode = childNode.clone({});
auto &layoutableClonedChildNode =
traitCast<YogaLayoutableShadowNode const &>(*clonedChildNode);
Expand Down
5 changes: 5 additions & 0 deletions ReactCommon/react/renderer/core/DynamicPropsUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "DynamicPropsUtilities.h"

namespace facebook::react {

folly::dynamic mergeDynamicProps(
folly::dynamic const &source,
folly::dynamic const &patch) {
Expand All @@ -30,4 +31,8 @@ folly::dynamic mergeDynamicProps(
return result;
}

RawProps mergeRawProps(folly::dynamic const &source, RawProps const &patch) {
return {mergeDynamicProps((folly::dynamic)source, (folly::dynamic)patch)};
}

} // namespace facebook::react
9 changes: 5 additions & 4 deletions ReactCommon/react/renderer/core/DynamicPropsUtilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
#pragma once

#include <folly/dynamic.h>
#include <react/renderer/core/RawProps.h>

namespace facebook {
namespace react {
namespace facebook::react {

folly::dynamic mergeDynamicProps(
folly::dynamic const &source,
folly::dynamic const &patch);

} // namespace react
} // namespace facebook
RawProps mergeRawProps(folly::dynamic const &source, RawProps const &patch);

} // namespace facebook::react
7 changes: 7 additions & 0 deletions ReactCommon/react/renderer/core/ShadowNodeFamily.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ class ShadowNodeFamily final {
void dispatchRawState(StateUpdate &&stateUpdate, EventPriority priority)
const;

/*
* Holds currently applied native props. `nullptr` if setNativeProps API is
* not used. It is used to backport setNativeProps API from the old
* architecture and will be removed in the future.
*/
mutable std::unique_ptr<folly::dynamic> nativeProps_DEPRECATED;

private:
friend ShadowNode;
friend ShadowNodeFamilyFragment;
Expand Down
11 changes: 11 additions & 0 deletions ReactCommon/react/renderer/scheduler/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,17 @@ void Scheduler::uiManagerDidDispatchCommand(
}
}

void Scheduler::setNativeProps_DEPRECATED(
const ShadowNode::Shared &shadowNode,
Props::Shared props) {
SystraceSection s("Scheduler::setNativeProps_DEPRECATED");

if (delegate_ != nullptr) {
auto shadowView = ShadowView(*shadowNode);
delegate_->setNativeProps_DEPRECATED(shadowView, std::move(props));
}
}

void Scheduler::uiManagerDidSendAccessibilityEvent(
const ShadowNode::Shared &shadowNode,
std::string const &eventType) {
Expand Down
3 changes: 3 additions & 0 deletions ReactCommon/react/renderer/scheduler/Scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ class Scheduler final : public UIManagerDelegate {
const ShadowNode::Shared &shadowNode,
std::string const &commandName,
folly::dynamic const &args) override;
void setNativeProps_DEPRECATED(
const ShadowNode::Shared &shadowNode,
Props::Shared props) override;
void uiManagerDidSendAccessibilityEvent(
const ShadowNode::Shared &shadowNode,
std::string const &eventType) override;
Expand Down
10 changes: 6 additions & 4 deletions ReactCommon/react/renderer/scheduler/SchedulerDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
#include <react/renderer/mounting/MountingCoordinator.h>
#include <react/renderer/mounting/ShadowView.h>

namespace facebook {
namespace react {
namespace facebook::react {

/*
* Abstract class for Scheduler's delegate.
Expand All @@ -41,6 +40,10 @@ class SchedulerDelegate {
std::string const &commandName,
folly::dynamic const &args) = 0;

virtual void setNativeProps_DEPRECATED(
const ShadowView &shadowView,
Props::Shared props) = 0;

virtual void schedulerDidSendAccessibilityEvent(
const ShadowView &shadowView,
std::string const &eventType) = 0;
Expand All @@ -56,5 +59,4 @@ class SchedulerDelegate {
virtual ~SchedulerDelegate() noexcept = default;
};

} // namespace react
} // namespace facebook
} // namespace facebook::react
54 changes: 49 additions & 5 deletions ReactCommon/react/renderer/uimanager/UIManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "UIManager.h"

#include <react/debug/react_native_assert.h>
#include <react/renderer/core/DynamicPropsUtilities.h>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/core/ShadowNodeFragment.h>
#include <react/renderer/debug/SystraceSection.h>
Expand Down Expand Up @@ -109,14 +110,29 @@ ShadowNode::Shared UIManager::cloneNode(
shadowNode.getFamily().getSurfaceId(), *contextContainer_.get()};

auto &componentDescriptor = shadowNode.getComponentDescriptor();
auto &family = shadowNode.getFamily();
auto props = ShadowNodeFragment::propsPlaceholder();

if (rawProps != nullptr) {
if (family.nativeProps_DEPRECATED != nullptr) {
family.nativeProps_DEPRECATED =
std::make_unique<folly::dynamic>(mergeDynamicProps(
(folly::dynamic)*rawProps, *family.nativeProps_DEPRECATED));

props = componentDescriptor.cloneProps(
propsParserContext,
shadowNode.getProps(),
mergeRawProps(*family.nativeProps_DEPRECATED, *rawProps));
} else {
props = componentDescriptor.cloneProps(
propsParserContext, shadowNode.getProps(), *rawProps);
}
}

auto clonedShadowNode = componentDescriptor.cloneShadowNode(
shadowNode,
{
/* .props = */
rawProps != nullptr
? componentDescriptor.cloneProps(
propsParserContext, shadowNode.getProps(), *rawProps)
: ShadowNodeFragment::propsPlaceholder(),
/* .props = */ props,
/* .children = */ children,
});

Expand Down Expand Up @@ -329,6 +345,34 @@ void UIManager::dispatchCommand(
}
}

void UIManager::setNativeProps_DEPRECATED(
ShadowNode::Shared const &shadowNode,
RawProps const &rawProps) const {
if (delegate_ != nullptr) {
auto &family = shadowNode->getFamily();
if (family.nativeProps_DEPRECATED) {
family.nativeProps_DEPRECATED =
std::make_unique<folly::dynamic>(mergeDynamicProps(
*family.nativeProps_DEPRECATED, (folly::dynamic)rawProps));
} else {
family.nativeProps_DEPRECATED =
std::make_unique<folly::dynamic>((folly::dynamic)rawProps);
}

PropsParserContext propsParserContext{
family.getSurfaceId(), *contextContainer_.get()};
auto &componentDescriptor =
componentDescriptorRegistry_->at(shadowNode->getComponentHandle());

auto props = componentDescriptor.cloneProps(
propsParserContext,
getNewestCloneOfShadowNode(*shadowNode)->getProps(),
RawProps(*family.nativeProps_DEPRECATED));

delegate_->setNativeProps_DEPRECATED(shadowNode, std::move(props));
}
}

void UIManager::sendAccessibilityEvent(
const ShadowNode::Shared &shadowNode,
std::string const &eventType) {
Expand Down
4 changes: 4 additions & 0 deletions ReactCommon/react/renderer/uimanager/UIManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ class UIManager final : public ShadowTreeDelegate {
std::string const &commandName,
folly::dynamic const &args) const;

void setNativeProps_DEPRECATED(
ShadowNode::Shared const &shadowNode,
RawProps const &rawProps) const;

void sendAccessibilityEvent(
const ShadowNode::Shared &shadowNode,
std::string const &eventType);
Expand Down
18 changes: 18 additions & 0 deletions ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,24 @@ jsi::Value UIManagerBinding::get(
});
}

if (methodName == "setNativeProps") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &,
const jsi::Value *arguments,
size_t) -> jsi::Value {
uiManager->setNativeProps_DEPRECATED(
shadowNodeFromValue(runtime, arguments[0]),
RawProps(runtime, arguments[1]));

return jsi::Value::undefined();
});
}

// Legacy API
if (methodName == "measureLayout") {
return jsi::Function::createFromHostFunction(
Expand Down
9 changes: 9 additions & 0 deletions ReactCommon/react/renderer/uimanager/UIManagerDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ class UIManagerDelegate {
std::string const &commandName,
folly::dynamic const &args) = 0;

/*
* Called when UIManager wants directly manipulate view on the mounting layer.
* This is a backport of setNativeProps from the old architecture and will be
* removed in the future.
*/
virtual void setNativeProps_DEPRECATED(
const ShadowNode::Shared &shadowNode,
Props::Shared props) = 0;

/*
* Called when UIManager wants to dispatch some accessibility event
* to the mounting layer. eventType is platform-specific and not all
Expand Down

0 comments on commit 1d3fa40

Please sign in to comment.