混合开发(OC)

声明类 实现协议

需要实现了RCTBridgeModule协议类,
导入#import <React/RCTBridgeModule.h>

实现协议需要包含这个宏,可以添加参数作为访问此模块的名字 通常不指定默认使用类名
RCT_EXPORT_MODULE();

声明原生方法给js调用

声明需要提供给React Native组件调用的方法,即导出的方法
RCT_EXPORT_METHOD()

//在OC中
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 CalendarManager = NativeModules.CalendarManager;
CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');

通常我们将多个参数使用一个字典进行存放

#import <React/RCTConvert.h>

RCT_EXPORT_METHOD(addEvent:(NSString *)name details:(NSDictionary *)details)
{
  NSString *location = [RCTConvert NSString:details[@"location"]];
  NSDate *time = [RCTConvert NSDate:details[@"time"]];
  ...
}

CalendarManager.addEvent('Birthday Party', {
  location: '4 Privet Drive, Surrey',
  time: date.toTime(),
  description: '...'
})

声明回调函数


//原生
//原生模块通常只应调用回调函数一次。但是,它可以保存callback并在将来调用
RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback)
{
  NSArray *events = ...
  callback(@[[NSNull null], events]);
}

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

//采用promise

//原生
RCT_REMAP_METHOD(findEvents,
                 resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
  NSArray *events = ...
  if (events) {
    resolve(events);
  } else {
    reject(error);
  }
}
//js调用
async function updateEvents() {
  try {
    var events = await CalendarManager.findEvents();
    this.setState({ events });
  } catch (e) {
    console.error(e);
  }
}

updateEvents();

多线程

生模块不应对自己被调用时所处的线程做任何假设。React Native在一个独立的串行GCD队列中调用原生模块的方法,但这属于实现的细节,并且可能会在将来的版本中改变。通过实现方法- (dispatch_queue_t)methodQueue,原生模块可以指定自己想在哪个队列中被执行。

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

如果一个操作需要花费很长时间,原生模块不应该阻塞住,而是应当声明一个用于执行操作的独立队列

- (dispatch_queue_t)methodQueue
{
  return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
}

在模块之间共享分发队列
methodQueue方法会在模块被初始化的时候被执行一次,然后会被React Native的桥接机制保存下来,所以你不需要自己保存队列的引用,除非你希望在模块的其它地方使用它。但是,如果你希望在若干个模块中共享同一个队列,则需要自己保存并返回相同的队列实例;仅仅是返回相同名字的队列是不行的。

导出常量

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

给JS发送事件

继承RCTEventEmitter,实现suppportEvents方法并调用self sendEventWithName:

#import <React/RCTEventEmitter.h>

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

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

JavaScript代码可以创建一个包含你的模块的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)
);
...
// 别忘了取消订阅,通常在componentWillUnmount生命周期方法中实现。
subscription.remove();

原生UI

原生视图需要被一个RCTViewManager来管理和创建。本质上都是单例 - React Native只会为每个管理器创建一个实例。它们创建原生的视图并提供给RCTUIManagerRCTUIManager则会反过来委托它们在需要的时候去设置和更新视图的属性。RCTViewManager还会代理视图的所有委托,并给JavaScript发回对应的事件

  1. 创建RCTViewManager的子类
  2. 添加RCT_EXPORT_MODULE()标记宏
  3. 实现-(UIView *)view方法

  4. 在js代码中将视图变为可用的React组件

// MapView.js
var { requireNativeComponent } = require('react-native');

// requireNativeComponent 自动把这个组件提供给 "RNTMapManager"
module.exports = requireNativeComponent('RNTMap', null);

声明属性

//声明属性
RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL)

//设置属性之后,在js中可以直接设置
<MapView pitchEnabled={false} />

//比较复杂的属性
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RNTMap)
{
  [view setRegion:json ? [RCTConvert MKCoordinateRegion:json] : defaultView.region animated:YES];
}