一、简介

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

03-05 17:09