和原生端通信
通过植入原生应用和原生 UI 组件两篇文档,我们学习了 React Native 和原生组件的互相整合。在整合 的过程中,我们会需要在两个世界间互相通信。有些方法已经在其他的指南中提到了,这篇文章总结了所有可行的技术。
简介
React Native 是从 React 中得到的灵感,因此基本的信息流是类似的。在 React 中信息是单向的。我们维护着组件层次,在其中每个组件都仅依赖于它父组件和自己的状态。通过属性(props)我们将信息从上而下的从父组件传递到子元素。如果一个祖先组件需要自己子孙的状态,推荐的方法是传递一个回调函数给对应的子元素。
React Native 也运用了相同的概念。只要我们完全在框架内构建应用,就可以通过属性和回调函数来调动整个应用。但是,当我们混合 React Native 和原生组件时,我们需要一些特殊的,跨语言的机制来传递信息。
属性
属性是最简单的跨组件通信。因此我们需要一个方法从原生组件传递属性到 React Native 或者从 React Native 到原生组件。
从原生组件传递属性到 React Native
我们使用RCTRootView
将 React Natvie 视图封装到原生组件中。RCTRootView
是一个UIView
容器,承载着 React Native 应用。同时它也提供了一个联通原生端和被托管端的接口。
通过RCTRootView
的初始化函数你可以将任意属性传递给 React Native 应用。参数initialProperties
必须是NSDictionary
的一个实例。这一字典参数会在内部被转化为一个可供 JS 组件调用的 JSON 对象。
NSArray *imageList = @[@"http://foo.com/bar1.png",
@"http://foo.com/bar2.png"];
NSDictionary *props = @{@"images" : imageList};
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"ImageBrowserApp"
initialProperties:props];
import React from 'react';
import {
View,
Image
} from 'react-native';
export default class ImageBrowserApp extends React.Component {
renderImage(imgURI) {
return (
<Image source={{uri: imgURI}} />
);
}
render() {
return (
<View>
{this.props.images.map(this.renderImage)}
</View>
);
}
}
RCTRootView
同样提供了一个可读写的属性appProperties
。在appProperties
设置之后,React Native 应用将会根据新的属性重新渲染。当然,只有在新属性和之前的属性有区别时更新才会被触发。
NSArray *imageList = @[@"http://foo.com/bar3.png",
@"http://foo.com/bar4.png"];
rootView.appProperties = @{@"images" : imageList};
你可以随时更新属性,但是更新必须在主线程中进行,读取则可以在任何线程中进行。
注意: 目前有一个已知问题,如果在 bridge 还没初始化完成前就设置 appProperties,设置可能会无效,具体讨论请见 https://github.com/facebook/react-native/issues/20115
更新属性时并不能做到只更新一部分属性。我们建议你自己封装一个函数来构造属性。
注意: 目前,最顶层的 RN 组件(即 registerComponent 方法中调用的那个)的
componentWillReceiveProps
和componentWillUpdateProps
方法在属性更新后不会触发。但是,你可以通过componentWillMount
访问新的属性值。