混合开发(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只会为每个管理器创建一个实例。它们创建原生的视图并提供给RCTUIManager
,RCTUIManager
则会反过来委托它们在需要的时候去设置和更新视图的属性。RCTViewManager还会代理视图的所有委托,并给JavaScript发回对应的事件
- 创建
RCTViewManager
的子类 - 添加
RCT_EXPORT_MODULE()
标记宏 实现
-(UIView *)view
方法在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];
}