一、简介
本文将通过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不久,如果本文中有错误的,请大家指出。同时也欢迎大家讨论更好的方式,谢谢。