一、简介
本文将通过Simple Demo的首页作为例子讲解React Native与Native之间通信。希望帮助刚接触RN的同学了解和选择合适的方式去传输数据。Simple Demo首页代码如下:
Simple Demo首页AdrHomePageReactView继承于AdrReactView;
AdrReactView.h
#import "AdrHomePageReactView.h"
@interface AdrHomePageReactView : AdrReactView
@end
AdrHomePageReactView.m
#import "AdrHomePageReactView.h"
@implementation AdrHomePageReactView
-(NSString *)componentModelName
{
return @"Homepage";
}
@end
AdrReactView.m
#import "AdrReactView.h"
#import <RCTRootView.h>
#import "AdrGlobalVar.h"
@implementation AdrReactView
- (instancetype)initWithProperties:(NSDictionary *)propertiesDic
{
self = [super init];
if (self) {
[self configureRCTRootView:propertiesDic];
}
return self;
}
- (void)configureRCTRootView:(NSDictionary *)propertiesDic
{
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:[AdrGlobalVar sharedAdrGlobalVar].bridge
moduleName:[self componentModelName]
initialProperties:propertiesDic];
rootView.frame = self.bounds;
[self addSubview:rootView];
}
- (NSString *)componentModelName
{
return @"";
}
@end
二、数据通信的方式
1.Native通过创建RCTRootView传入属性
通过Native代码创建RN的View时传入Properties,由Native传入Properties(字典),RN通过constructor(props)读取到对应keyValue。
AdrHomePageReactView初始化的时候传入@{userName:Kilolo}
//RN任意门测试主页面
AdrHomePageReactView *homepageView = [[AdrHomePageReactView alloc] initWithProperties:@{@"userName":@"Kilolo"}];
homepageView.frame = self.view.bounds;
[self.view addSubview:homepageView];
RN在构造函数接收到props
constructor(props){
super(props);
this.state = {
NonUserSystemDisable:true,
FYZTDisable:false,
YZTDisable:false,
LogoutDisable:true,
};
console.log('initProps:'+this.props.userName);
}
输出结果:
2.Native导出常量
ViewController导出Module及Constants
RCT_EXPORT_MODULE();
-(NSDictionary<NSString *,id> *)constantsToExport
{
return @{@"userPassword":@"mima123"};
}
RN代码:
import {
NativeModules,
} from 'react-native';
var ViewController = NativeModules.ViewController;
componentDidMount(){
console.log('componentDidMount');
console.log('userPassword:'+ViewController.userPassword);
}
输出结果:
3.Native导出方法
Native,在ViewController中导出Module及通过RCT_EXPORT_METHOD导出方法给RN,
Native代码:
RCT_EXPORT_MODULE();
//测试页面
RCT_EXPORT_METHOD(rnShowFunctionTestView)
{
[self showFunctionTestView];
};
RN可以传数据给Native,调用Native的方法,RN调用代码:
ViewController.rnShowFunctionTestView()
4.RN注册通知,Native发送通知
Native增加属性bridge,注意属性名bridge不可以随意更改。因为RN会对bridge自动赋值;若更改了,则需要自己找到当前RN页面所使用的bridge。
Native根据登录的状态发通知给RN,修改RN中登出按钮是否可点击。
@property(strong,nonatomic) RCTBridge *bridge;
[self.bridge.eventDispatcher sendAppEventWithName:kAdrssoticketChangeNotification body:@{@"isResponse":logoutBtn.enabled?@"true":@"false"}];
RN在componentDidMount的生命周期中注册通知,在componentWillUnMount中移除通知。
componentDidMount(){
var registerLogoutBottonResponseNotification = NativeAppEventEmitter.addListener(
'AdrssoticketChangeNotification',
(reminder) => {
if (reminder.isResponse === 'true'){
this.setState({LogoutDisable:false});
}else if (reminder.isResponse === 'false'){
this.setState({LogoutDisable:true});
}
console.log('reminder.isResponse:'+reminder.isResponse);
}
);
}
componentWillUnmount()
{
registerLogoutBottonResponseNotification.remove();
}
5.回调方式
RN调用Native方法,Native通过block把数据传给RN。
这里有两种回调方式,一种是RCTResponseSenderBlock回调,第二种是RCTPromiseResolveBlock回调,RCTPromiseRejectBlock回调。个人建议后者方式比较好,因为Promise能处理复杂的异步操作,而且不用等待另一个异步操作完成后才能执行另一个异步操作,还有书写代码优雅等特点。
(1)RCTResponseSenderBlock
Native中代码:
RCT_REMAP_METHOD(_scanQRCode:(RCTResponseSenderBlock)callback)
{
dispatch_async(dispatch_get_main_queue(), ^{
callback();
});
}
RCTResponseSenderBlock只接受一个参数,传递给RN回调函数的参数数组.
RN中的代码:
AdrInterface._scanQRCode((events) => {
console.log(evets);
})
(2)RCTPromiseResolveBlock和RCTPromiseRejectBlock
RCTPromiseResolveBlock和RCTPromiseRejectBlock,则对应的RN方法就会返回一个Promise对象。
Native代码:
RCT_REMAP_METHOD(_scanQRCode,resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self scanQRCode:nil callBack:^(NSDictionary *info, AdrECallBackStatus status) {
if (status == AdrECallBackStatus_Succ) {
resolve(info);
}else if (status == AdrECallBackStatus_Fail){
NSError *error = [[NSError alloc] init];
reject(@"code",info[@"error"],error);
}
}];
});
}
RN代码:
AdrInterface._scanQRCode()
.then(value => {console.log('qrcode:',value['value'])})
.catch(error => {console.log('error:')})
.then()括号中的方法是成功回调;.catch()是失败回调,能捕捉到_scanQRCode( )和.then( )抛出异常。建议不要把成功和失败的方法都传入then作为参数,这样不能捕捉到.then的失败异常。
另:Promise对象能使用Promise.all()和Promise.race()等处理复杂的异步操作,详情请参考http://es6.ruanyifeng.com/#do...
三、结语
鉴于接触RN和ES6不久,如果本文中有错误的,请大家指出。同时也欢迎大家讨论更好的方式,谢谢。