进阶 reactnative.dev 2026-10-11 06:04:21 · 29 阅读
第23章 原生端与 React Native 之间的通信机制
版本:0.77
原生端与 React Native 之间的通信
在“与现有应用集成”指南和“原生 UI 组件”指南中,我们学习了如何将在原生组件中嵌入 React Native,反之亦然。当我们将原生组件与 React Native 组件混合使用 时,最终会发现有在这两个世界之间进行通信的需求。其他指南中已提及一些实现方式,本文将对可用技术进行汇总。
引言
React Native 受 React 启发,因此信息流的基本理念类似。React 中的数据流向是单向的。我们维护一个组件层级结构,其中每个组件仅依赖于其父组件及其自身内部状态。我们通过属性实现这一点:数据以自顶向下的方式从父组件传递给子组件。如果祖先组件依赖于其后代的 状态,则需要向下传递一个回调函数,供后代用于更新祖先组件。
同样的概念也适用于 React Native。只要我们在框架内构建应用程序,就可以通过属性和回调来驱动应用。但是,当混合使用 React Native 和原生组件时,我们需要特定的跨语言机制,以便在它们之间传递信息。
属性
属性是组件间通信最直观的方式。因此,我们需要实现从原生到 React Native,以及从 React Native 到原生的属性传递。
从原生传递属性到 React Native
通过在主 Activity 中提供 ReactActivityDelegate 的自定义实现,可以将属性传递给 React Native 应用。该实现应重写 getLaunchOptions 方法,以返回包含所需属性的 Bundle。
Java Kotlin Java
public class MainActivity extends ReactActivity {
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected Bundle getLaunchOptions() {
Bundle initialProperties = new Bundle();
ArrayList imageList = new ArrayList(Arrays.asList(
"https://dummyimage.com/600x400/ffffff/000000.png",
"https://dummyimage.com/600x400/000000/ffffff.png"
));
initialProperties.putStringArrayList("images", imageList);
return initialProperties;
}
};
}
}
Kotlinclass 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
}
}
}
}
React TSXimport React from 'react';
import {View, Image} from 'react-native';
export default class ImageBrowserApp extends React.Component {
renderImage(imgURI) {
return ;
}
render() {
return{this.props.images.map(this.renderImage)} ;
}
}
ReactRootView 提供了一个可读写的属性 appProperties。设置 appProperties 后,React Native 应用会用新的属性重新渲染。只有当新属性与之前的属性不同时,才会执行更新。 JavaKotlinJavaBundle updatedProps = mReactRootView.getAppProperties();
ArrayList imageList = new ArrayList(Arrays.asList(
"https://dummyimage.com/600x400/ff0000/000000.png",
"https://dummyimage.com/600x400/ffffff/ff0000.png"
));
updatedProps.putStringArrayList("images", imageList);
mReactRootView.setAppProperties(updatedProps);
Kotlinvar updatedProps: Bundle = reactRootView.getAppProperties()
var imageList = arrayListOf("https://dummyimage.com/600x400/ff0000/000000.png", "https://dummyimage.com/600x400/ffffff/ff0000.png") 随时都可以更新属性,但必须在主线程上执行更新操作。获取属性则可以在任意线程中调用。 目前无法只更新部分属性。建议将此功能封装到你自己的包装器中。 注意: 目前,当属性更新后,顶层 React Native 组件的 JS 函数 componentWillUpdateProps 不会被调用。但你可以在 componentDidMount 函数中访问新的属性。 将属性从 React Native 传递到原生层 本文详细探讨了暴露原生组件属性的问题。简而言之,需要在 JavaScript 中体现的属性必须通过带有 @ReactProp 注解的 setter 方法暴露出来,然后在 React Native 中像使用普通组件一样使用它们。 属性限制的说明 跨语言属性的主要缺点是它不支持回调,而回调允许我们处理自底向上的数据绑定。想象一下,你有一个小型 React Native 视图,希望因 JavaScript 操作而从原生父视图中移除。由于信息需要自底向上流动,无法通过属性实现此功能。 虽然我们有某种跨语言回调机制(详见此处),但这并不总是我们需要的方式。主要问题在于,这些回调并非设计用来作为属性传递的。相反,这种机制允许我们从 JavaScript 触发原生操作,并在 JavaScript 中处理该操作的结果。 其他跨语言交互方式(事件和原生模块) 如上一章所述,使用属性存在一些局限性。有时仅靠属性不足以驱动应用逻辑,我们需要更灵活的解决方案。本章介绍了 React Native 中可用的其他通信技巧。它们既可用于内部通信(React Native 中 JS 层与原生层之间),也可用于外部通信(React Native 与应用中“纯原生”部分之间)。 React Native 支持跨语言函数调用。你可以从 JavaScript 执行自定义原生代码,反之亦然。不幸的是,根据我们在哪一侧工作,实现目标的方式各不相同。对于原生层,我们使用事件机制来调度 JavaScript 中处理函数的执行;而对于 React Native,我们直接调用原生模块导出的方法。 从原生层调用 React Native 函数(事件) 事件在本文中有详细说明。请注意,使用事件无法保证执行时间,因为事件是在独立线程中处理的。 事件功能强大,因为它们允许我们在没有组件引用的情况下更改 React Native 组件。但在使用时需注意一些潜在陷阱: 由于事件可以从任何地方发送,可能会在项目引入依赖关系,形成“意大利面式”依赖。 事件共享命名空间,因此可能会遇到名称冲突。由于冲突无法在静态检查中检测出来,调试起来比较困难。 如果你使用同一个 React Native 组件的多个实例,并希望在事件视角下区分它们,通常引入标识符并将其与事件一起传递(可以使用原生视图的 reactTag 作为标识符)。 从 React Native 调用原生函数(原生模块) 原生模块是在 JavaScript 中可用的 Java/Kotlin 类。通常,每个 JavaScript 桥接会创建每个模块的一个实例。它们可以向 React Native 导出任意函数和常量。本文已对其进行了详细讲解。 警告: 所有原生模块共享同一个命名空间。创建新模块时请注意避免名称冲突。
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected Bundle getLaunchOptions() {
Bundle initialProperties = new Bundle();
ArrayList
"https://dummyimage.com/600x400/ffffff/000000.png",
"https://dummyimage.com/600x400/000000/ffffff.png"
));
initialProperties.putStringArrayList("images", imageList);
return initialProperties;
}
};
}
}
Kotlinclass 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
}
}
}
}
React TSXimport React from 'react';
import {View, Image} from 'react-native';
export default class ImageBrowserApp extends React.Component {
renderImage(imgURI) {
return
}
render() {
return
}
}
ReactRootView 提供了一个可读写的属性 appProperties。设置 appProperties 后,React Native 应用会用新的属性重新渲染。只有当新属性与之前的属性不同时,才会执行更新。 JavaKotlinJavaBundle updatedProps = mReactRootView.getAppProperties();
ArrayList
"https://dummyimage.com/600x400/ff0000/000000.png",
"https://dummyimage.com/600x400/ffffff/ff0000.png"
));
updatedProps.putStringArrayList("images", imageList);
mReactRootView.setAppProperties(updatedProps);
Kotlinvar updatedProps: Bundle = reactRootView.getAppProperties()
var imageList = arrayListOf("https://dummyimage.com/600x400/ff0000/000000.png", "https://dummyimage.com/600x400/ffffff/ff0000.png") 随时都可以更新属性,但必须在主线程上执行更新操作。获取属性则可以在任意线程中调用。 目前无法只更新部分属性。建议将此功能封装到你自己的包装器中。 注意: 目前,当属性更新后,顶层 React Native 组件的 JS 函数 componentWillUpdateProps 不会被调用。但你可以在 componentDidMount 函数中访问新的属性。 将属性从 React Native 传递到原生层 本文详细探讨了暴露原生组件属性的问题。简而言之,需要在 JavaScript 中体现的属性必须通过带有 @ReactProp 注解的 setter 方法暴露出来,然后在 React Native 中像使用普通组件一样使用它们。 属性限制的说明 跨语言属性的主要缺点是它不支持回调,而回调允许我们处理自底向上的数据绑定。想象一下,你有一个小型 React Native 视图,希望因 JavaScript 操作而从原生父视图中移除。由于信息需要自底向上流动,无法通过属性实现此功能。 虽然我们有某种跨语言回调机制(详见此处),但这并不总是我们需要的方式。主要问题在于,这些回调并非设计用来作为属性传递的。相反,这种机制允许我们从 JavaScript 触发原生操作,并在 JavaScript 中处理该操作的结果。 其他跨语言交互方式(事件和原生模块) 如上一章所述,使用属性存在一些局限性。有时仅靠属性不足以驱动应用逻辑,我们需要更灵活的解决方案。本章介绍了 React Native 中可用的其他通信技巧。它们既可用于内部通信(React Native 中 JS 层与原生层之间),也可用于外部通信(React Native 与应用中“纯原生”部分之间)。 React Native 支持跨语言函数调用。你可以从 JavaScript 执行自定义原生代码,反之亦然。不幸的是,根据我们在哪一侧工作,实现目标的方式各不相同。对于原生层,我们使用事件机制来调度 JavaScript 中处理函数的执行;而对于 React Native,我们直接调用原生模块导出的方法。 从原生层调用 React Native 函数(事件) 事件在本文中有详细说明。请注意,使用事件无法保证执行时间,因为事件是在独立线程中处理的。 事件功能强大,因为它们允许我们在没有组件引用的情况下更改 React Native 组件。但在使用时需注意一些潜在陷阱: 由于事件可以从任何地方发送,可能会在项目引入依赖关系,形成“意大利面式”依赖。 事件共享命名空间,因此可能会遇到名称冲突。由于冲突无法在静态检查中检测出来,调试起来比较困难。 如果你使用同一个 React Native 组件的多个实例,并希望在事件视角下区分它们,通常引入标识符并将其与事件一起传递(可以使用原生视图的 reactTag 作为标识符)。 从 React Native 调用原生函数(原生模块) 原生模块是在 JavaScript 中可用的 Java/Kotlin 类。通常,每个 JavaScript 桥接会创建每个模块的一个实例。它们可以向 React Native 导出任意函数和常量。本文已对其进行了详细讲解。 警告: 所有原生模块共享同一个命名空间。创建新模块时请注意避免名称冲突。