在用RN开发的时候,遇到一些情况也需要来扩展原生的组件,就需要与OC来交互。
通讯的机制与流程
属性
通过属性(properties)我们将信息从上而下的从父组件传递到子元素。如果一个祖先组件需要自己子孙的状态,推荐的方法是传递一个回调函数给对应的子元素。
属性是最简单的跨组件通信。因此我们需要一个方法从原生组件传递属性到React Native或者从React Native到原生组件。
RCTRootView
RCTRootView将React Natvie视图封装到原生组件中。RCTRootView是一个UIView容器,承载着React Native应用。同时它也提供了一个联通原生端和被托管端的接口。
原生传递/更新属性->RN
传递属性:参数initialProperties,这个RCTRootView的初始化函数的参数来完成。initialProperties必须是NSDictionary的一个实例。这一字典参数会在内部被转化为一个可供JS组件调用的JSON对象。
1 | NSArray *imageList = @[@"http://foo.com/bar1.png", |
就可以把ImageBrowserApp作为RN的一个组件。AppRegistry.registerComponent('ImageBrowserApp', () => ImageBrowserApp);
更新属性:RCTRootView同样提供了一个可读写的属性appProperties。在appProperties设置之后,React Native应用将会根据新的属性重新渲染。当然,只有在新属性和之前的属性有区别时更新才会被触发。
1 | NSArray *imageList = @[@"http://foo.com/bar3.png", |
RN传递属性->原生
在你自定义的原生组件中通过RCT_CUSTOM_VIEW_PROPERTY宏导出属性,就可以直接在React Native中使用,就好像它们是普通的React Native组件一样。
自定义Native API组件(原生模块)
在React Native中,一个“原生模块”就是一个实现了“RCTBridgeModule”协议的Objective-C类,其中RCT是ReaCT的缩写。
JS中也可以使用的Objective-C的类。每一个模块的实例都是在每一次通过JS bridge通信时创建的。
模块和方法的定义
模块类就是一个实现了RCTBridgeModule协议的类。
1 | // CalendarManager.h |
为了实现RCTBridgeModule协议,你的类需要包含RCT_EXPORT_MODULE()宏.
RCT_EXPORT_MODULE();//向系统注册模块
RCT_REMAP_METHOD();//暴露模块方法
普通调用
- OC中声明要给Javascript导出的方法,通过RCT_EXPORT_METHOD()宏来实现:
JavaScript和OC之间要通信,完成数据类型的转化,标准的JSON的类型都是支持的。
现在从Javascript里可以这样调用:
首先,先导入和声明原生模块:
1 | // 导入NativeModules |
// 调用原生方法CalendarManager.addEvent('调用testNormalEvent方法', '测试普通调用')
参数类型
RCT_EXPORT_METHOD 支持所有标准JSON类型,包括:
- string (NSString)
- number (NSInteger, float, double, CGFloat, NSNumber)
- boolean (BOOL, NSNumber)
- array (NSArray) 包含本列表中任意类型
- object (NSDictionary) 包含string类型的键和本列表中任意类型的值
- function (RCTResponseSenderBlock)
除此以外,任何RCTConvert类支持的的类型也都可以使用(参见RCTConvert了解更多信息)。RCTConvert还提供了一系列辅助函数,用来接收一个JSON值并转换到原生Objective-C类型或类。
回调函数
原生模块还支持一种特殊的参数——回调函数。它提供了一个函数来把返回值传回给JavaScript。
(^RCTResponseSenderBlock)(NSArray )接收多个参数的回调函数
(^RCTRespomseErrorBlock)(NSError )接受错误参数的回调函数
(^RCTPromiseResolveBlock)(id result):处理Promise Resolve
(^RCTPromiseRejectBlock)(NSError *):处理Promise Reject
Promises
使用promise来简化代码,搭配ES2016(ES7)标准的async/await语法则效果更佳。如果桥接原生方法的最后两个参数是RCTPromiseResolveBlock和RCTPromiseRejectBlock,则对应的JS方法就会返回一个Promise对象。
给Javascript发送事件–RCTBridge与RCTEventDispatcher
即使没有被JavaScript调用,本地模块也可以给JavaScript发送事件通知。最直接的方式是使用eventDispatcher:
在Javascript中订阅事件–NativeAppEventEmitter.addListener
1 | import { NativeAppEventEmitter } from 'react-native'; |
原生UI组件封装
React Native代码与OC代码的互通
为了实现消息互通,需要建立一个原生语言模块负责与React Native桥接
OC代码向React Native发送消息有两种方式:
- 通过回调接口。这种方式要求React Native代码先将接口传递给OC代码,然后OC代码才可以通过这个回调接口向React Native代码发送消息。
- 通过eventDispatcher向React Native模块发送事件。这样能够做到OC代码主动向React Native发送消息。