React Native自定义组件和输出方法

简介

如果你看的这一篇,需要你对ReactNative的开发有一定的了解,此文讲的是在ReactNative提供的组件不能满足需求,或者native用于较成熟的组件想要输出,那么就需要用到自定义组件了.
通过该文,我们也可以对native和JS交互方式进行初步了解,关于输出方法内部实现,我们下一篇再剖.

Native module

native module就是实现了RCTBridgeModule协议的OC类.RCT就是ReaCT的缩写.

具体步骤如下

  1. 引入#import <React/RCTBridgeModule.h>类,然后遵守RCTBridgeModule协议.
  2. 实现RCT_EXPORT_MODULE(customName)方法. customName是自定义的组件名,如果不填默认为当前类名.这个组件名是用于向JS输出.

输出组建后,默认不会向JS输出任何方法,想要输出方法,需要自定义实现方法输出,使用宏RCT_EXPORT_METHOD ()

RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
  RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}

对于JS端,调用时就可以如下

import {NativeModules} from 'react-native';
var CustomName = NativeModules.CalendarManager;
CustomName.addEvent('Birthday Party', '4 Privet Drive, Surrey');

注意

向JS输出的方法名,是RCT_EXPORT_METHOD之后,第一个冒号之前的名字.如果native已经暴露了多个冒号之前同名的方法,RN提供了RCT_REMAP_METHOD ()来制定方法名.

另外一点, RCT_EXPORT_METHOD回调进入的方法,默认并不在主线程,如果想要进行主线程的方法调用,需要手动进行dispatch_async(dispatch_get_main_queue(), ^{});回到主线程

RCT_EXPORT_METHOD参数

RCT_EXPORT_METHOD支持如下的参数类型

  • string (NSString)
  • number (NSInteger, float, double, CGFloat, NSNumber)
  • boolean (BOOL, NSNumber)
  • array (NSArray) of any types from this list
  • object (NSDictionary) with string keys and values of any type from this list
  • function (RCTResponseSenderBlock)

也支持所有RCTConvert支持的类型.

回调

native module支持回调类型RCTResponseSenderBlock

RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback)
{
  NSArray *events = ...
  callback(@[[NSNull null], events]);
}

RCTResponseSenderBlock只支持一个参数:一个包含了多个参数的数组.在JS端可以如下,默认第一个参数是error.当没有错误时error为空.

CalendarManager.findEvents((error, events) => {
  if (error) {
    console.error(error);
  } else {
    this.setState({events: events});
  }
});

native module只能调用一次回调.如果想传递错误,通过RCTUtils.h类中的RCTMakeError来创建.

Promise

Promise是用于实现异步操作async/await的工具类.如果最后一个参数类型为RCTPromiseResolveBlockRCTPromiseRejectBlock,JS端会返回一个promise对象,进行一步操作.

RCT_REMAP_METHOD(findEvents,
                 findEventsWithResolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
  NSArray *events = ...
  if (events) {
    resolve(events);
  } else {
    NSError *error = ...
    reject(@"no_events", @"There were no events", error);
  }
}

JS端因为获取的是promise对象,可以使用await关键字进行异步调用并等待结果

async function updateEvents() {
  try {
    var events = await CalendarManager.findEvents();

    this.setState({events});
  } catch (e) {
    console.error(e);
  }
}

updateEvents();

关于线程

JS执行native module是在一个单独的线程实现的,可以通过- (dispatch_queue_t)methodQueue来控制.如果返回主线程,所有执行的方法会在主线程被执行.

- (dispatch_queue_t)methodQueue
{
  return dispatch_get_main_queue();
}

方法methodQueue之后在组件初始化时被调用一次.

输出实例

除了可以输出方法,还有输出实例.

- (NSDictionary *)constantsToExport
{
  return @{ @"firstDayOfTheWeek": @"Monday" };
}

在JS端可以直接获取console.log(CalendarManager.firstDayOfTheWeek);
只有在初始化时实例输出才是有效的,如果在运行时修改constantsToExport是不会影响JS环境的数据的.

输出枚举

通过typedef NS_ENUM()定义的枚举,可以通过增加RCTConvert的扩展来完成

@implementation RCTConvert (StatusBarAnimation)
  RCT_ENUM_CONVERTER(UIStatusBarAnimation, (@{ @"statusBarAnimationNone" : @(UIStatusBarAnimationNone),
                                               @"statusBarAnimationFade" : @(UIStatusBarAnimationFade),
                                               @"statusBarAnimationSlide" : @(UIStatusBarAnimationSlide)}),
                      UIStatusBarAnimationNone, integerValue)
@end

之后就可以通过输出属性和方法等方式在JS中使用了.

native向JS发方法

想给JS发方法,可以继承类RCTEventEmitter,实现supportedEvents方法,然后通过调用self sendEventWithName:即可.

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
  return @[@"EventReminder"];
}

- (void)calendarEventReminderReceived:(NSNotification *)notification
{
  NSString *eventName = notification.userInfo[@"name"];
  [self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];
}

JS端可以通过NativeEventEmitter进行注册和调用

import { NativeEventEmitter, NativeModules } from 'react-native';
const { CalendarManager } = NativeModules;

const calendarManagerEmitter = new NativeEventEmitter(CalendarManager);

const subscription = calendarManagerEmitter.addListener(
  'EventReminder',
  (reminder) => console.log(reminder.name)
);
...
// Don't forget to unsubscribe, typically in componentWillUnmount
subscription.remove();

注意取消订阅,一般在componentWillUnmount内执行.
客户端可以通过一些方式获取JS注册和移除订阅的事件,来优化只在有订阅者的情况下才发送事件.

// Will be called when this module's first listener is added.
-(void)startObserving {
    hasListeners = YES;
    // Set up any upstream listeners or background tasks as necessary
}

// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
    hasListeners = NO;
    // Remove upstream listeners, stop unnecessary background tasks
}

资料

native modules官文

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容