React Native教程 中文版<四> Native模块交互

Native 模块(iOS)

有时一个应用程序需要访问平台 API,React Native 并没有相应的封装器。也许你想重用现有的一些 Objective——C 或 C++ 代码,无需在 JavaScript 上重新实现。或者写一些高性能,多线程的代码,如图像处理、网络堆栈,数据库或渲染。

我们设计 React Native,这样可以为你写真正的本地代码,并且能够访问整个平台。这是一个更高级的特性,且我们并不期望它成为通常开发过程的一部分,但是它的存在是至关重要的。如果 React Native 不支持你需要的本地特性,那么你应该能够自己构建它。

这是一个更高级的指南,展示了如何构建一个本地模块。它假设读者知道 Objective-C(Swift 还没有支持)和核心库(Foundation,UIKit)。

iOS 日历模块的例子

本指南将使用iOS 日历 API 的例子。假设我们希望能够从 JavaScript 访问 iOS 日历。

Native 模块只是一个 Objectve-C 类,实现了RCTBridgeModule协议。如果你想知道,RCT 是 ReaCT 的一个简称。

// CalendarManager.h
    #import "RCTBridgeModule.h"
    "RCTLog.h"
    @interface CalendarManager : NSObject <RCTBridgeModule>
    @end

React Native 不会向 JavaScript 公开任何CalendarManager方法,除非有明确的要求。幸运的是有了RCT_EXPORT,这会非常简单:

// CalendarManager.m
    @implementation CalendarManager
    - (void)addEventWithName:(NSString *)name location:(NSString *)location
    {
        RCT_EXPORT();
        RCTLogInfo(@"Pretending to create an event %@ at %@",name,location);
    }
     现在从你的 JavaScript 文件中,你可以像这样调用方法:

var CalendarManager = require('NativeModules').CalendarManager;
    CalendarManager.addEventWithName('Birthday Party','4 Privet Drive,Surrey');

注意,导出的方法名称是从 Objective-C 选择器的第一部分中生成的。有时它会产生一个非惯用的 JavaScript 名称(就像在我们的例子中的那个)。你可以通过为RCT_EXPORT提供一个可选参数更改名字,如RCT_EXPORT(addEvent)

方法返回的类型应该是void。React Native 桥是异步的,所以向 JavaScript 传递结果的唯一方法是使用回调或 emitting 事件(见下文)。

参数类型

React Native 支持多种参数类型,可以从 JavaScript 代码传递到 native 模块:

  • 字符串型(NSString)
  • 数字型(NSIntegerfloat,double,244)">CGFloat,244)">NSNumber)
  • 布尔型(BOOL,244)">NSNumber)
  • 这个列表中任何类型的数组(NSArray)
  • 这个列表中任何类型的字符串键和值的映射(NSDictionary)
  • 函数(RCTResponseSenderBlock)

在我们的CalendarManager示例中,如果我们想把事件日期传递到 native,我们必须将它转换成一个字符串或一个数字:

- (NSString *)location date:(NSInteger)secondsSinceUnixEpoch
    {
        RCT_EXPORT(addEvent);
        NSDate *date = [NSDate dateWithTimeIntervalSince1970:secondsSinceUnixEpoch];
    }

随着CalendarManager.addEvent方法变得越来越复杂,参数的数量将会增加。其中一些可能是可选的。在这种情况下对改变 API 一点来接受事件属性的字典是值得考虑的,如:

"RCTConvert.h"
    - (NSString *)name details:(NSDictionary *)details
    {
        RCT_EXPORT(addEvent);
        NSString *location = [RCTConvert NSString:details[@"location"]]; // ensure location is a string
        ...
    }

并且从 JavaScript 调用它:

CalendarManager.addEvent(time: date.toTime(),description: '...'
    })

注意:关于数组和映射

React Ntive 没有为这些结构中值的类型提供任何担保。你的 native 模块可能期望一个字符串数组,但如果 JavaScript 调用你的包含数字和字符串数组的方法,你会得到带有NSNumberNSStringNSArray。检查数组/映射值类型是开发人员的责任 (助手方法见RCTConvert)。

回调

警告

本节比其他更具有实验性,围绕回调我们没有得到一组最佳实践。

Native 模块还支持一种特殊的参数——回调。在大多数情况下它是用来向 JavaScript 提供函数调用结果的。

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

RCTResponseSenderBlock只接受一个参数——参数的数组传递给 JavaScript 的回调。在本例中,我们使用节点的惯例来为 error 和其他的——函数的结果设置第一个参数。

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

Native 模块应该只调用它的回调一次。然而,它可以将回调作为 ivar 存储并稍后调用回调。这种模式通常用于包装需要委托的 iOS 的 APIs。请看RCTAlertManager

如果你想向 JavaScript 传递 error ——如对象,使用RCTUtils.hRCTMakeError

实现 native 模块

Native 模块应该没有任何关于什么线程正在被调用的假设。React Native 在一个单独的串行 GCD 队列中调用 native 模块方法,但这是一个实现细节,可能会改变。如果 native 模块需要调用 main-thread-only iOS API,它应该在主队列安排操作:

- (void)addEventWithName:(NSString *)name callback:(RCTResponseSenderBlock)callback
{
  RCT_EXPORT(addEvent);
  dispatch_async(dispatch_get_main_queue(),^{
    // Call iOS API on main thread ... // You can invoke callback from any thread/queue callback(@[...]);
  });
}

同样的方法,如果操作要很长时间才能完成,native 模块不应该阻塞。使用dispatch_async在后台队列中安排耗费大的工作是一个好主意。

导出常量

Native 模块可以在运行时向 JavaScript 导出立即可用的常量。导出一些初始数据是有用的,否则这些初始数据需要往返的桥梁。

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

JavaScript 能够立即使用这些值:

console.log(CalendarManager.firstDayOfTheWeek);

注意,只有在初始化时常量才能被导出,所以如果你在运行时改变了constantsToExport的值,它不会影响 JavaScript 环境。

发送事件到JavaScript

Native 模块可以在不被直接调用的情况下向 JavaScript 发送事件信号。最简单的方法是使用eventDispatcher

"RCTBridge.h"
    "RCTEventDispatcher.h"
    CalendarManager
    @synthesize bridge = _bridge;
    - (void)calendarEventReminderReceived:(NSNotification *)notification
    {
        NSString *eventName = notification.userInfo[@"name"];
        [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder"
                                               body:@{@"name": eventName}];
    }
     JavaScript 代码可以订阅这些事件:

var subscription = DeviceEventEmitter.addListener(
  'EventReminder',(reminder) => console.log(reminder.name)
);
...
// Don't forget to unsubscribe subscription.remove();

更多的向 JavaScript 发送事件的例子,请看RCTLocationObserver

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom