ReactNative原理与核心知识点
React Native特点
跨平台
使用js写出页面组件代码被React框架统一转成Virtual DOM树,Virtual DOM树是UI结构的一层抽象,可以被转换成任何支持端的UI视图。
ReactNative框架将Virtual DOM 转成原APP的UIView树。
热修复
ReactNative的产物bundle包,bundle包中包含的为RN业务所需要的所有资源,包括代码和资源。bundle的加载方式是APP启动时从后台下载,然后通过js虚拟机执行的。
所以可以将每次业务迭代修改后的代码上传到服务,进行用户无感知版本更新。
注意:
bundle中的业务代码不能修改APP现有的原生行为,不能调用私有API,不然禁止上架。
bundle包中的js是经过babel转义后的普通js,而非jsx语法糖。

JS与Native交互的基本原理
JS引擎
iOS侧使用的JavaScriptCore作为bundle产物的js执行引擎。
JS与Native交互的基本原理很简单,就是在JS的全局上下文添加成员变量。原生调用JS是JS在JS上下文中添加方法成员变量,然后原生调用。JS调用原生是原生往JS上下文中添加方法成员变量,然后JS调用。
JS调用原生
通过将block对象赋值给js全局上下文中的全局变量,js内部调用这个全局方法进行执行。
ctx[
@"NativeMethod"
] = ^(NSString *name) {
// do something
return
someResult
}
原生调用JS
先创建一个JS上下文对象,在上下文中添加方法的全局变量。原生获取上下文的全局变量Value, 然后调用,执行这个JS方法。
// 创建一个ctx的JS上下文
JSContent *ctx = [[JSContent alloc] init];
// 创建一个变量name
[ctx evaluateScript:
@"var name = 'jack'"
];
// 创建一个方法
[ctx evaluateScript:
@"var sayHi = function(name) { return 'hello ' + name }"
];
// 通过ctx上下文对象,获取到hello方法
JSValue *sayHiUnction = ctx[
@"sayHi"
];
// 运行js方法
JSValue *greetings = [sayHiUnction callWithArguments:@[
@"jack"
];
// hello jack
ReactNative框架中原生与JS的调用基本思路也是这种,只不过考虑到大量的Native对象注册会污染js引擎中的上下文,增加了一层Bridge。
原生和JS之间的交互都是通过Bridge这个通道,通过里面的几个基础方法进行交互。原生与JS的交互是异步的。
另外,Facebook为了提升RN框架中JS的执行效率,专门推出了一个JS引擎 Hermes, 在关键指标上,相比于JSCore,V8提升了不少,比较易于RN框架集成。
ReactNative核心知识
RCTBridge: ReactNative中原生与JS交互的通道
RCTBridge用于给js引擎提供原生扩展接口。将原生功能如定位,3D等通过Bridge将其封装成JS接口,然后注入到js引擎的上下文中。
RN框架启动的简单流程为:首先将js代码加载到内存,然后创建RCTBridge实例,然后创建RCTRootContentView内容展示的容器视图,然后调用JS上下文中AppRegistry对象的runApplication方法,并将@[moduleName, appParameters]组件名,参数传递给JS。
// RCTRootView.m
- (
void
)javaScriptDidLoad:(
NSNotification
*)notification
{
RCTAssertMainQueue();
RCTBridge *bridge = notification.userInfo[@
"bridge"
];
if
(bridge != _contentView.bridge) {
[
self
bundleFinishedLoading:bridge];
}
}
- (
void
)bundleFinishedLoading:(RCTBridge *)bridge
{
// 省略创建RCTRootContentView...
[
self
runApplication:bridge];
// 省略添加一个RCTRootContentView...
}
- (
void
)runApplication:(RCTBridge *)bridge
{
NSString
*moduleName = _moduleName ?: @
""
;
// 这里是@"NewProject"
NSDictionary
*appParameters = @{
@
"rootTag"
: _contentView.reactTag,
@
"initialProps"
: _appProperties ?: @{},
};
[bridge enqueueJSCall:@
"AppRegistry"
method:@
"runApplication"
args:@[moduleName, appParameters]
completion:
NULL
];
}
原生调用JS
在JS上下文中,调用JS的方式是通过方法:global.batchedBridge.callFunctionReturnFlushedQueue

所以RN在原生侧的的JS引擎的封装对象中使用成员变量保存了这个JS的函数指针,原生调用JS时,通过传递参数 moduleid 和 methodid 完成方法的调用。
void
JSIExecutor
::
bindBridge
() {
std
::
call_once
(
bindFlag_
, [
this
] {
SystraceSection
s
(
"JSIExecutor::bindBridge (once)"
);
Value
batchedBridgeValue
=
runtime_
-
>
global
().
getProperty
(*
runtime_
,
"__fbBatchedBridge"
);
if
(
batchedBridgeValue
.
isUndefined
() || !
batchedBridgeValue
.
isObject
()) {
throw
JSINativeException
(
"Could not get BatchedBridge, make sure your bundle is packaged correctly"
);
}
Object
batchedBridge
=
batchedBridgeValue
.
asObject
(*
runtime_
);
callFunctionReturnFlushedQueue_
=
batchedBridge
.
getPropertyAsFunction
(
*
runtime_
,
"callFunctionReturnFlushedQueue"
);
invokeCallbackAndReturnFlushedQueue_
=
batchedBridge
.
getPropertyAsFunction
(
*
runtime_
,
"invokeCallbackAndReturnFlushedQueue"
);
flushedQueue_
=
batchedBridge
.
getPropertyAsFunction
(*
runtime_
,
"flushedQueue"
);
});
}
JS调用原生
JS调用原生通常是通过原生主动处理_eventQueue中的事件,特殊情况会直接调用原生注册给JS的nativeFlushQueueImmediate方法, 并传递moduleName 、methodName、callback 参数给这个方法完成调用。

void
JSIExecutor
::
initializeRuntime
() {
SystraceSection
s
(
"JSIExecutor::initializeRuntime"
);
runtime_
-
>
global
().
setProperty
(
*
runtime_
,
"nativeModuleProxy"
,
Object
::
createFromHostObject
(
*
runtime_
,
std
::
make_shared
<
NativeModuleProxy
>
(
nativeModules_
)));
runtime_
-
>
global
().
setProperty
(
*
runtime_
,
"nativeFlushQueueImmediate"
,
Function
::
createFromHostFunction
(
*
runtime_
,
PropNameID
::
forAscii
(*
runtime_
,
"nativeFlushQueueImmediate"
),
1
,
[
this
](
jsi
::
Runtime
&
,
const
jsi
::
Value
&
,
const
jsi
::
Value
*
args
,
size_t
count
) {
if
(
count
!=
1
) {
throw
std
::
invalid_argument
(
"nativeFlushQueueImmediate arg count must be 1"
);
}
callNativeModules
(
args
[
0
],
false
);
return
Value
::
undefined
();
}));
runtime_
-
>
global
().
setProperty
(
*
runtime_
,
"nativeCallSyncHook"
,
Function
::
createFromHostFunction
(
*
runtime_
,
PropNameID
::
forAscii
(*
runtime_
,
"nativeCallSyncHook"
),
1
,
[
this
](
jsi
::
Runtime
&
,
const
jsi
::
Value
&
,
const
jsi
::
Value
*
args
,
size_t
count
) {
return
nativeCallSyncHook
(
args
,
count
); }));
runtime_
-
>
global
().
setProperty
(
*
runtime_
,
"globalEvalWithSourceUrl"
,
Function
::
createFromHostFunction
(
*
runtime_
,
PropNameID
::
forAscii
(*
runtime_
,
"globalEvalWithSourceUrl"
),
1
,
[
this
](
jsi
::
Runtime
&
,
const
jsi
::
Value
&
,
const
jsi
::
Value
*
args
,
size_t
count
) {
return
globalEvalWithSourceUrl
(
args
,
count
); }));
if
(
runtimeInstaller_
) {
runtimeInstaller_
(*
runtime_
);
}
bool
hasLogger
(
ReactMarker
::
logTaggedMarker
);
if
(
hasLogger
) {
ReactMarker
::
logMarker
(
ReactMarker
::
CREATE_REACT_CONTEXT_STOP
);
}
}
Virtual DOM 虚拟DOM
虚拟DOM的特点
1.用于描述页面的UI结构:在作用上虚拟DOM和普通的DOM是一样的。
2.平台无关性:虚拟DOM表示的UI结构是对UI的一层抽象,它是平台无关的。具体的UI渲染是交个具体的平台渲染引擎进行的,如iOS,安卓自身的渲染引擎。
虚拟DOM对标签的定义
虚拟DOM把标签分为2类:原子型标签,组合型标签。
原子型标签是平台支持型的基础标签,如果RCTView, RCTText。对应浏览器页面中,原子型标签有h1,li,div等。
组合型标签是用户自定义的组件,它在虚拟DOM中对应的是自定义标签构造器函数,页面渲染时调用这个构造函数,创建一个实例,然后调用实例的render方法,组合型标签的render方法内会把组合标签进行拆解,最后拆解成基本的原子型标签。
var
ele
= {
...
type
:
type
,
// 元素的类型
key
:
key
,
// 元素key标示
ref
:
ref
,
// 元素的引用
props
:
props
,
// 元素的参数,包含children
...
}
// example 1
<
div
>
hello
<
/
div
>
// 会被描述为
{
type
: '
div
',
props
: {
children
: ['
hello
']
}
}
// example 2
<
CustomerComponents
/
>
// 会被描述为
{
type
:
CustomerComponents
}
UI渲染
RN框架与浏览器的对比:在浏览器中,JS通过调用DOM API创建UI视图。在RN中,JS通过调用RCTUIManager来创建iOS,Android移动端的UI视图。
RN的UI渲染是基于虚拟DOM的,通过根据不同的平台调用不同平台的Bridge, Brideg再调用不同平台的的RCTUIManager进行UI的创建。
其他
三条线程
RN内部有三条线程在同时运行着:Shadow Thread, JS Thread, UI Thread。
JS Thread:JS线程,负责JS与原生的交互,它们的交互是异步的,每次调用都是将block放入队列中,等js代码执行完后,读取事件队列进行处理。
UI Thread:UI主线程,负责页面的交互与渲染, 由RCTUIManager使用。
Shadow Thread: 负责将flex布局转成Native的布局,由yago引擎使用。
三个队列
RN框架内,原生与JS的交换类型分两种:UI和事件,这2这种事件的处理都是异步的,它们都是将事件顺序放置到队列中,在合适的时机被调用。
事件的处理在RCTBridge中处理,UI的处理在RCTUIManager中处理。
JS调用原生异步事件队列:_eventQueue队列
原生调用JS异步事件队列:_pendingCalls队列
UI更新异步事件处理队列:_pendingUIBlocks队列
JSI
javascript interface js虚拟机通用接口层,是针对JS引擎封装的上层API框架,使用JSI做JS引擎调用的优点:
1.底部可以任意替换JS引擎而不影响上层JS引擎的使用。如:可以任意替换JavaScript Core, V8等。
2.通过JSI,JavaScript可以持有C++宿主对象的引用,所以可以直接调用原生方法(UIView, NativeModule),它与现在统一使用Bridge这个通道和消息异步调用比起来,提高了消息发送的及时性,避免了消息队列执行的等待。
React Native核心知识在框架中的使用
React Native核心功能在RN项目启动时会进行各自的初始化,生成bundle运行上下文。在类型上可以分为2类:
1.JS与原生的事件处理:创建RCTBridge桥接通道。
2.UI交互与更新的事件处理:创建RCTRootView容器视图。
APP启动,React Native运行环境初始化。
- (
BOOL
)
application
:(
UIApplication
*)
application
didFinishLaunchingWithOptions
:(
NSDictionary
*)
launchOptions
{
if
(!
self
.
bridge
) {
self
.
bridge
= [
self
createBridgeWithDelegate
:
self
launchOptions
:
launchOptions
];
}
NSDictionary
*
initProps
= [
self
prepareInitialProps
];
UIView
*
rootView
= [
self
createRootViewWithBridge
:
self
.
bridge
moduleName
:
self
.
moduleName
initProps
:
initProps
];
if
(@
available
(
iOS
13.0
, *)) {
rootView
.
backgroundColor
= [
UIColor
systemBackgroundColor
];
}
else
{
rootView
.
backgroundColor
= [
UIColor
whiteColor
];
}
self
.
window
= [[
UIWindow
alloc
]
initWithFrame
:[
UIScreen
mainScreen
].
bounds
];
UIViewController
*
rootViewController
= [
self
createRootViewController
];
rootViewController
.
view
=
rootView
;
self
.
window
.
rootViewController
=
rootViewController
;
[
self
.
window
makeKeyAndVisible
];
return
YES
;
}
JS与原生的事件处理:创建RCTBridge桥接通道
RCTBridge的主要逻辑是在batchedBridge中,主要初始化流程为:
1.初始化Native Modules
2.创建Native Modules配置表
3.准备JS引擎工厂,创建JS引擎
4.将Modules配置信息注册到JS引擎中
5.加载boundle代码
6.执行boundle代码
- (
void
)
setUp
{
RCT_PROFILE_BEGIN_EVENT
(
0
, @
"-[RCTBridge setUp]"
,
nil
);
//_performanceLogger日志工具初始化
//_bundleURL获取
//batchedBridge创建
self
.
batchedBridge
= [[
bridgeClass
alloc
]
initWithParentBridge
:
self
];
[
self
.
batchedBridge
start
];
RCT_PROFILE_END_EVENT
(
RCTProfileTagAlways
, @
""
);
}
batchedBridge
是
RCTCXXBridge
,它的初始化方法如下:
- (
instancetype
)
initWithParentBridge
:(
RCTBridge
*)
bridge
{
RCTAssertParam
(
bridge
);
if
((
self
= [
super
initWithDelegate
:
bridge
.
delegate
bundleURL
:
bridge
.
bundleURL
moduleProvider
:
bridge
.
moduleProvider
launchOptions
:
bridge
.
launchOptions
])) {
_parentBridge
=
bridge
;
_performanceLogger
= [
bridge
performanceLogger
];
registerPerformanceLoggerHooks
(
_performanceLogger
);
/**
* Set Initial State
*/
_valid
=
YES
;
_loading
=
YES
;
_moduleRegistryCreated
=
NO
;
_pendingCalls
= [
NSMutableArray
new
];
_displayLink
= [
RCTDisplayLink
new
];
_moduleDataByName
= [
NSMutableDictionary
new
];
_moduleClassesByID
= [
NSMutableArray
new
];
_moduleDataByID
= [
NSMutableArray
new
];
_objCModuleRegistry
= [
RCTModuleRegistry
new
];
[
_objCModuleRegistry
setBridge
:
self
];
_bundleManager
= [
RCTBundleManager
new
];
[
_bundleManager
setBridge
:
self
];
_viewRegistry_DEPRECATED
= [
RCTViewRegistry
new
];
[
_viewRegistry_DEPRECATED
setBridge
:
self
];
_callableJSModules
= [
RCTCallableJSModules
new
];
[
_callableJSModules
setBridge
:
self
];
[
RCTBridge
setCurrentBridge
:
self
];
[[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:@
selector
(
handleMemoryWarning
)
name
:
UIApplicationDidReceiveMemoryWarningNotification
object
:
nil
];
RCTLogSetBridgeModuleRegistry
(
_objCModuleRegistry
);
RCTLogSetBridgeCallableJSModules
(
_callableJSModules
);
}
return
self
;
}
- (
void
)
start
{
RCT_PROFILE_BEGIN_EVENT
(
RCTProfileTagAlways
, @
"-[RCTCxxBridge start]"
,
nil
);
[[
NSNotificationCenter
defaultCenter
]
postNotificationName
:
RCTJavaScriptWillStartLoadingNotification
object
:
_parentBridge
userInfo
:@{@
"bridge"
:
self
}];
//启动JS线程
_jsThread
= [[
NSThread
alloc
]
initWithTarget
:[
self
class
]
selector
:@
selector
(
runRunLoop
)
object
:
nil
];
_jsThread
.
name
=
RCTJSThreadName
;
_jsThread
.
qualityOfService
=
NSOperationQualityOfServiceUserInteractive
;
#
if
RCT_DEBUG
_jsThread
.
stackSize
*=
2
;
#
endif
[
_jsThread
start
];
dispatch_group_t
prepareBridge
=
dispatch_group_create
();
[
_performanceLogger
markStartForTag
:
RCTPLNativeModuleInit
];
//1.初始化Native Modules
[
self
registerExtraModules
];
//2.创建Native Modules配置表
// Initialize all native modules that cannot be loaded lazily
(
void
)[
self
_initializeModules
:
RCTGetModuleClasses
()
withDispatchGroup
:
prepareBridge
lazilyDiscovered
:
NO
];
[
self
registerExtraLazyModules
];
[
_performanceLogger
markStopForTag
:
RCTPLNativeModuleInit
];
// This doesnt really do anything. The real work happens in initializeBridge.
_reactInstance
.
reset
(
new
Instance
);
__weak
RCTCxxBridge
*
weakSelf
=
self
;
// 3.准备JS引擎工厂,创建JS引擎
std
::
shared_ptr
<
JSExecutorFactory
>
executorFactory
;
if
(!
self
.
executorClass
) {
if
([
self
.
delegate
conformsToProtocol
:@
protocol
(
RCTCxxBridgeDelegate
)]) {
id
<
RCTCxxBridgeDelegate
>
cxxDelegate
= (
id
<
RCTCxxBridgeDelegate
>
)
self
.
delegate
;
executorFactory
= [
cxxDelegate
jsExecutorFactoryForBridge
:
self
];
}
// 4.将Modules配置信息注册到JS引擎中
if
(!
executorFactory
) {
auto
installBindings
=
RCTJSIExecutorRuntimeInstaller
(
nullptr
);
#
if
RCT_USE_HERMES
executorFactory
=
std
::
make_shared
<
HermesExecutorFactory
>
(
installBindings
);
#
else
executorFactory
=
std
::
make_shared
<
JSCExecutorFactory
>
(
installBindings
);
#
endif
}
}
else
{
id
<
RCTJavaScriptExecutor
>
objcExecutor
= [
self
moduleForClass
:
self
.
executorClass
];
executorFactory
.
reset
(
new
RCTObjcExecutorFactory
(
objcExecutor
, ^(
NSError
*
error
) {
if
(
error
) {
[
weakSelf
handleError
:
error
];
}
}));
}
//_turboModuleRegistry是一个TurboModule注册表,TurboModule是JS在RN中的一种优化方式,将常用的JS代码编译成可执行代码,提高执行速度。
/**
* id<RCTCxxBridgeDelegate> jsExecutorFactory may create and assign an id<RCTTurboModuleRegistry> object to
* RCTCxxBridge If id<RCTTurboModuleRegistry> is assigned by this time, eagerly initialize all TurboModules
*/
if
(
_turboModuleRegistry
&
&
RCTTurboModuleEagerInitEnabled
()) {
for
(
NSString
*
moduleName
in
[
_turboModuleRegistry
eagerInitModuleNames
]) {
[
_turboModuleRegistry
moduleForName
:[
moduleName
UTF8String
]];
}
for
(
NSString
*
moduleName
in
[
_turboModuleRegistry
eagerInitMainQueueModuleNames
]) {
if
(
RCTIsMainQueue
()) {
[
_turboModuleRegistry
moduleForName
:[
moduleName
UTF8String
]];
}
else
{
id
<
RCTTurboModuleRegistry
>
turboModuleRegistry
=
_turboModuleRegistry
;
dispatch_group_async
(
prepareBridge
,
dispatch_get_main_queue
(), ^{
[
turboModuleRegistry
moduleForName
:[
moduleName
UTF8String
]];
});
}
}
}
// Dispatch the instance initialization as soon as the initial module metadata has
// been collected (see initModules)
dispatch_group_enter
(
prepareBridge
);
[
self
ensureOnJavaScriptThread
:^{
[
weakSelf
_initializeBridge
:
executorFactory
];
dispatch_group_leave
(
prepareBridge
);
}];
// 5.加载boundle代码
// Load the source asynchronously, then store it for later execution.
dispatch_group_enter
(
prepareBridge
);
__block
NSData
*
sourceCode
;
[
self
loadSource
:^(
NSError
*
error
,
RCTSource
*
source
) {
if
(
error
) {
[
weakSelf
handleError
:
error
];
}
sourceCode
=
source
.
data
;
dispatch_group_leave
(
prepareBridge
);
}
onProgress
:^(
RCTLoadingProgress
*
progressData
) { }];
// 模块和js代码加载完成后,执行js代码
// Wait for both the modules and source code to have finished loading
dispatch_group_notify
(
prepareBridge
,
dispatch_get_global_queue
(
QOS_CLASS_USER_INTERACTIVE
,
0
), ^{
RCTCxxBridge
*
strongSelf
=
weakSelf
;
if
(
sourceCode
&
&
strongSelf
.
loading
) {
// 6.执行boundle代码
[
strongSelf
executeSourceCode
:
sourceCode
sync
:
NO
];
}
});
RCT_PROFILE_END_EVENT
(
RCTProfileTagAlways
, @
""
);
}
初始化Native Modules与创建Native Modules配置表
把本地的RN模块都收集起来,包括RN框架自带的和用户自定义的,将模块信息保存到Bridge的变量中,用于与JS交换。
_moduleDataByName = [NSMutableDictionary new];
_moduleClassesByID = [NSMutableArray new];
_moduleDataByID = [NSMutableArray new];
JS发送消息到Native时,通过- (id)moduleForName:(const char *)moduleName;查询到模块详情,进行模块调用。
_objCModuleRegistry = [RCTModuleRegistry new];
[_objCModuleRegistry setBridge:self];
准备JS引擎工厂,创建JS引擎与将Modules配置信息注册到JS引擎中
RN将Native Modules信息收集完成后保存到成员变量中,这个成员变量是一个数组。使用moduleConfig保存模块的模块名,方法名。然后将这些数据注入到JS引擎中。
JS调用原生时,通过模块名,方法名,参数调用原生方法。在原生调用JS时,会将调用放入_pendingCalls队列中,进行异步执行。而JS调原生是将调用放入到_eventQueue队列中,进行异步执行。
JS可以通过方法nativeFlushQueueImmediate直接调用Native,但是一般JS不会这样做,而是等原生自己去_eventQueue队列中自己去取任务做处理。
// js thread only (which surprisingly can be the main thread, depends on used JS executor)
- (
void
)
flushEventsQueue
{
[
_eventQueueLock
lock
];
NSDictionary
*
events
=
_events
;
_events
= [
NSMutableDictionary
new
];
NSMutableArray
*
eventQueue
=
_eventQueue
;
_eventQueue
= [
NSMutableArray
new
];
_eventsDispatchScheduled
=
NO
;
[
_eventQueueLock
unlock
];
for
(
NSNumber
*
eventId
in
eventQueue
) {
[
self
dispatchEvent
:
events
[
eventId
]];
}
}
UI交互与更新的事件处理:创建RCTRootView容器视图
RCTRootView为RN页面的入口,在RCTRootView初始化过程中,会创建RCTRootContentView作为内容视图放置在RCTRootView的底部作为根视图。
RCTRootContentView的初始化方法中,在uiManager中将RCTRootContentView注册成根视图。
- (
instancetype
)
initWithFrame
:(
CGRect
)
frame
bridge
:(
RCTBridge
*)
bridge
reactTag
:(
NSNumber
*)
reactTag
sizeFlexiblity
:(
RCTRootViewSizeFlexibility
)
sizeFlexibility
{
if
((
self
= [
super
initWithFrame
:
frame
])) {
_bridge
=
bridge
;
self
.
reactTag
=
reactTag
;
_sizeFlexibility
=
sizeFlexibility
;
_touchHandler
= [[
RCTTouchHandler
alloc
]
initWithBridge
:
_bridge
];
[
_touchHandler
attachToView
:
self
];
[
_bridge
.
uiManager
registerRootView
:
self
];
}
return
self
;
}
RCTUIManager是RN中UI的管理者,它负责处理所有与UI相关的事情,如:注入到JS中的创建View方法。
在createView方法中可以看到,RN对View的操作都是双份的,分别作用在RCTShadowView和UIView上。RCTShadowView和UIView的关系类似于虚拟DOM和DOM的关系。
RCTShadowView是一个虚拟DOM树,是一个结构体,用于描述视图的样式和事件,比较轻量级。
在RN中当调用setState更新组件状态时,就会生成一个新的虚拟DOM,然后RN将新的虚拟DOM与旧的虚拟DOM进行Diff对比,生成差异对象,然后遍历差异对象,将所有的改动更新到UI上。而更新到了Native是先更新到RCTShadowView上,等合适的时候再统一更新到UI上。
UI更新操作也是异步的,更新任务被放置在_pendingUIBlocks队列上,在UI变化时或Bridge批出来结束时刷新这个队列。
RCT_EXPORT_METHOD
(
createView
: (
nonnull
NSNumber
*)
reactTag
viewName
: (
NSString
*)
viewName
rootTag
: (
nonnull
NSNumber
*)
rootTag
props
: (
NSDictionary
*)
props
)
{
RCTComponentData
*
componentData
=
_componentDataByName
[
viewName
];
if
(
componentData
==
nil
) {
RCTLogError
(@
"No component found for view with name \"%@\""
,
viewName
);
}
// Register shadow view
RCTShadowView
*
shadowView
= [
componentData
createShadowViewWithTag
:
reactTag
];
if
(
shadowView
) {
[
componentData
setProps
:
props
forShadowView
:
shadowView
];
_shadowViewRegistry
[
reactTag
] =
shadowView
;
RCTShadowView
*
rootView
=
_shadowViewRegistry
[
rootTag
];
RCTAssert
(
[
rootView
isKindOfClass
:[
RCTRootShadowView
class
]] || [
rootView
isKindOfClass
:[
RCTSurfaceRootShadowView
class
]],
@
"Given `rootTag` (%@) does not correspond to a valid root shadow view instance."
,
rootTag
);
shadowView
.
rootView
= (
RCTRootShadowView
*)
rootView
;
}
// Dispatch view creation directly to the main thread instead of adding to
// UIBlocks array. This way, it doesnt get deferred until after layout.
__block
UIView
*
preliminaryCreatedView
=
nil
;
void
(^
createViewBlock
)(
void
) = ^{
// Do nothing on the second run.
if
(
preliminaryCreatedView
) {
return
;
}
preliminaryCreatedView
= [
componentData
createViewWithTag
:
reactTag
rootTag
:
rootTag
];
if
(
preliminaryCreatedView
) {
self
-
>
_viewRegistry
[
reactTag
] =
preliminaryCreatedView
;
}
};
// We cannot guarantee that asynchronously scheduled block will be executed
// *before* a block is added to the regular mounting process (simply because
// mounting process can be managed externally while the main queue is
// locked).
// So, we positively dispatch it asynchronously and double check inside
// the regular mounting block.
RCTExecuteOnMainQueue
(
createViewBlock
);
[
self
addUIBlock
:^(
__unused
RCTUIManager
*
uiManager
,
__unused
NSDictionary
<
NSNumber
*,
UIView
*
>
*
viewRegistry
) {
createViewBlock
();
if
(
preliminaryCreatedView
) {
[
componentData
setProps
:
props
forView
:
preliminaryCreatedView
];
}
}];
[
self
_shadowView
:
shadowView
didReceiveUpdatedProps
:[
props
allKeys
]];
}