Native 与 React Native 之间的通信
在 与现有应用集成指南 和 原生 UI 组件指南 中,我们学习了如何将 React Native 嵌入到原生组件中,反之亦然。当我们混合原生和 React Native 组件时,我们最终会发现这两个世界之间需要进行通信。其他指南中已经提到了实现这一目标的一些方法。本文总结了可用的技术。
¥In Integrating with Existing Apps guide and Native UI Components guide we learn how to embed React Native in a native component and vice versa. When we mix native and React Native components, we'll eventually find a need to communicate between these two worlds. Some ways to achieve that have been already mentioned in other guides. This article summarizes available techniques.
介绍
¥Introduction
React Native 受到了 React 的启发,因此信息流的基本思想是相似的。React 中的流程是单向的。我们维护组件的层次结构,其中每个组件仅依赖于其父组件及其自身的内部状态。我们用属性来做到这一点:数据以自上而下的方式从父级传递到子级。如果祖级组件依赖于其后代的状态,则应该传递一个回调以供后代使用来更新祖级。
¥React Native is inspired by React, so the basic idea of the information flow is similar. The flow in React is one-directional. We maintain a hierarchy of components, in which each component depends only on its parent and its own internal state. We do this with properties: data is passed from a parent to its children in a top-down manner. If an ancestor component relies on the state of its descendant, one should pass down a callback to be used by the descendant to update the ancestor.
同样的概念也适用于 React Native。只要我们纯粹在框架内构建应用,我们就可以使用属性和回调来驱动我们的应用。但是,当我们混合使用 React Native 和原生组件时,我们需要一些特定的跨语言机制来允许我们在它们之间传递信息。
¥The same concept applies to React Native. As long as we are building our application purely within the framework, we can drive our app with properties and callbacks. But, when we mix React Native and native components, we need some specific, cross-language mechanisms that would allow us to pass information between them.
属性
¥Properties
属性是跨组件通信最直接的方式。因此,我们需要一种方法来将属性从原生传递到 React Native,以及从 React Native 传递到原生。
¥Properties are the most straightforward way of cross-component communication. So we need a way to pass properties both from native to React Native, and from React Native to native.
将属性从原生传递到 React Native
¥Passing properties from native to React Native
你可以通过在主活动中提供 ReactActivityDelegate
的自定义实现来将属性向下传递给 React Native 应用。此实现应覆盖 getLaunchOptions
以返回具有所需属性的 Bundle
。
¥You can pass properties down to the React Native app by providing a custom implementation of ReactActivityDelegate
in your main activity. This implementation should override getLaunchOptions
to return a Bundle
with the desired properties.
- Java
- Kotlin
public class MainActivity extends ReactActivity {
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected Bundle getLaunchOptions() {
Bundle initialProperties = new Bundle();
ArrayList<String> imageList = new ArrayList<String>(Arrays.asList(
"https://dummyimage.com/600x400/ffffff/000000.png",
"https://dummyimage.com/600x400/000000/ffffff.png"
));
initialProperties.putStringArrayList("images", imageList);
return initialProperties;
}
};
}
}
class MainActivity : ReactActivity() {
override fun createReactActivityDelegate(): ReactActivityDelegate {
return object : ReactActivityDelegate(this, mainComponentName) {
override fun getLaunchOptions(): Bundle {
val imageList = arrayListOf("https://dummyimage.com/600x400/ffffff/000000.png", "https://dummyimage.com/600x400/000000/ffffff.png")
val initialProperties = Bundle().apply { putStringArrayList("images", imageList) }
return initialProperties
}
}
}
}
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>;
}
}
ReactRootView
提供读写属性 appProperties
。设置 appProperties
后,React Native 应用将使用新属性重新渲染。仅当新更新的属性与之前的属性不同时才会执行更新。
¥ReactRootView
provides a read-write property appProperties
. After appProperties
is set, the React Native app is re-rendered with new properties. The update is only performed when the new updated properties differ from the previous ones.
- Java
- Kotlin
Bundle updatedProps = mReactRootView.getAppProperties();
ArrayList<String> imageList = new ArrayList<String>(Arrays.asList(
"https://dummyimage.com/600x400/ff0000/000000.png",
"https://dummyimage.com/600x400/ffffff/ff0000.png"
));
updatedProps.putStringArrayList("images", imageList);
mReactRootView.setAppProperties(updatedProps);
var updatedProps: Bundle = reactRootView.getAppProperties()
var imageList = arrayListOf("https://dummyimage.com/600x400/ff0000/000000.png", "https://dummyimage.com/600x400/ffffff/ff0000.png")
随时更新属性就可以了。但是,更新必须在主线程上执行。你可以在任何线程上使用 getter。
¥It is fine to update properties anytime. However, updates have to be performed on the main thread. You use the getter on any thread.
无法一次只更新几个属性。我们建议你将其构建到你自己的封装器中。
¥There is no way to update only a few properties at a time. We suggest that you build it into your own wrapper instead.
注意:目前,顶层 RN 组件的 JS 函数
componentWillUpdateProps
在 prop 更新后不会被调用。但是,你可以在componentDidMount
功能中访问新属性。¥Note: Currently, JS function
componentWillUpdateProps
of the top level RN component will not be called after a prop update. However, you can access the new props incomponentDidMount
function.
将属性从 React Native 传递到 Native
¥Passing properties from React Native to native
暴露原生组件属性的问题在 本文 中有详细介绍。简而言之,要在 JavaScript 中体现的属性需要公开为带有 @ReactProp
注解的 setter 方法,然后在 React Native 中使用它们,就像该组件是普通的 React Native 组件一样。
¥The problem exposing properties of native components is covered in detail in this article. In short, properties that are to be reflected in JavaScript needs to be exposed as setter method annotated with @ReactProp
, then use them in React Native as if the component was an ordinary React Native component.
属性限制
¥Limits of properties
跨语言属性的主要缺点是它们不支持回调,而回调允许我们处理自下而上的数据绑定。想象一下,你有一个小的 RN 视图,你希望通过 JS 操作将其从原生父视图中删除。使用 props 无法做到这一点,因为信息需要自下而上。
¥The main drawback of cross-language properties is that they do not support callbacks, which would allow us to handle bottom-up data bindings. Imagine you have a small RN view that you want to be removed from the native parent view as a result of a JS action. There is no way to do that with props, as the information would need to go bottom-up.
尽管我们有跨语言回调(此处描述)的样式,但这些回调并不总是我们需要的。主要问题是它们不打算作为属性传递。相反,这种机制允许我们从 JS 触发原生操作,并在 JS 中处理该操作的结果。
¥Although we have a flavor of cross-language callbacks (described here), these callbacks are not always the thing we need. The main problem is that they are not intended to be passed as properties. Rather, this mechanism allows us to trigger a native action from JS, and handle the result of that action in JS.
其他跨语言交互方式(事件和原生模块)
¥Other ways of cross-language interaction (events and native modules)
如前一章所述,使用属性有一些限制。有时属性不足以驱动我们应用的逻辑,我们需要一个提供更大灵活性的解决方案。本章介绍了 React Native 中可用的其他通信技术。它们可用于内部通信(JS 和 RN 中的原生层之间)以及外部通信(RN 和应用的 '纯本土的' 部分之间)。
¥As stated in the previous chapter, using properties comes with some limitations. Sometimes properties are not enough to drive the logic of our app and we need a solution that gives more flexibility. This chapter covers other communication techniques available in React Native. They can be used for internal communication (between JS and native layers in RN) as well as for external communication (between RN and the 'pure native' part of your app).
React Native 使你能够执行跨语言函数调用。你可以从 JS 执行自定义原生代码,反之亦然。不幸的是,根据我们正在努力的方面,我们以不同的方式实现相同的目标。对于本地人 - 在 JS 中,我们使用事件机制来调度处理函数的执行,而对于 React Native,我们直接调用原生模块导出的方法。
¥React Native enables you to perform cross-language function calls. You can execute custom native code from JS and vice versa. Unfortunately, depending on the side we are working on, we achieve the same goal in different ways. For native - we use events mechanism to schedule an execution of a handler function in JS, while for React Native we directly call methods exported by native modules.
从原生(事件)调用 React Native 函数
¥Calling React Native functions from native (events)
事件在 本文 中有详细描述。请注意,使用事件并不能保证执行时间,因为事件是在单独的线程上处理的。
¥Events are described in detail in this article. Note that using events gives us no guarantees about execution time, as the event is handled on a separate thread.
事件非常强大,因为它们允许我们更改 React Native 组件而无需引用它们。然而,在使用它们时,你可能会陷入一些陷阱:
¥Events are powerful, because they allow us to change React Native components without needing a reference to them. However, there are some pitfalls that you can fall into while using them:
-
由于事件可以从任何地方发送,因此它们可以将意大利面条式的依赖引入到你的项目中。
¥As events can be sent from anywhere, they can introduce spaghetti-style dependencies into your project.
-
事件共享命名空间,这意味着你可能会遇到一些名称冲突。不会静态检测冲突,这使得它们难以调试。
¥Events share namespace, which means that you may encounter some name collisions. Collisions will not be detected statically, which makes them hard to debug.
-
如果你使用同一 React Native 组件的多个实例,并且希望从事件的角度区分它们,则可能需要引入标识符并将它们与事件一起传递(你可以使用原生视图的
reactTag
作为标识符) 。¥If you use several instances of the same React Native component and you want to distinguish them from the perspective of your event, you'll likely need to introduce identifiers and pass them along with events (you can use the native view's
reactTag
as an identifier).
从 React Native 调用原生函数(原生模块)
¥Calling native functions from React Native (native modules)
原生模块是 JS 中可用的 Java/Kotlin 类。通常,每个 JS 桥都会创建每个模块的一个实例。他们可以将任意函数和常量导出到 React Native。它们已在 本文 中详细介绍。
¥Native modules are Java/Kotlin classes that are available in JS. Typically one instance of each module is created per JS bridge. They can export arbitrary functions and constants to React Native. They have been covered in detail in this article.
警告:所有原生模块共享相同的命名空间。创建新名称时请注意名称冲突。
¥Warning: All native modules share the same namespace. Watch out for name collisions when creating new ones.