跳到主要内容
版本:0.79

在你的原生组件上调用原生函数

基础指南 中,你已经探索了如何创建新组件,如何从 JS 端传递属性到原生端,以及如何从原生端向 JS 发射事件。

自定义组件还可以命令式地调用原生代码中实现的一些函数,以实现一些更高级的功能,例如以编程方式重新加载网页。

在本指南中,你将学习如何实现这一点,通过使用一个新概念:原生命令 (Native Commands)。

本指南从 原生组件 指南开始,并假设你熟悉该指南,并且熟悉 Codegen

1. 更新你的组件规范

第一步是更新组件规范以声明 NativeCommand

更新 WebViewNativeComponent.ts 如下:

Demo/specs/WebViewNativeComponent.ts
import type {HostComponent, ViewProps} from 'react-native';
import type {BubblingEventHandler} from 'react-native/Libraries/Types/CodegenTypes';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
+import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';

type WebViewScriptLoadedEvent = {
result: 'success' | 'error';
};

export interface NativeProps extends ViewProps {
sourceURL?: string;
onScriptLoaded?: BubblingEventHandler<WebViewScriptLoadedEvent> | null;
}

+interface NativeCommands {
+ reload: (viewRef: React.ElementRef<HostComponent<NativeProps>>) => void;
+}

+export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
+ supportedCommands: ['reload'],
+});

export default codegenNativeComponent<NativeProps>(
'CustomWebView',
) as HostComponent<NativeProps>;

这些更改要求你:

  1. react-native 导入 codegenNativeCommands 函数。这指示 codegen 它必须为 NativeCommands 生成代码
  2. 定义一个包含我们想在原生中调用的方法的接口。所有原生命令必须有一个类型为 React.ElementRef 的第一个参数。
  3. 导出 Commands 变量,它是调用 codegenNativeCommands 的结果,传递支持命令的列表。
注意

在 TypeScript 中,React.ElementRef 已弃用。正确的类型实际上是 React.ComponentRef。但是,由于 Codegen 中的一个 bug,使用 ComponentRef 会导致应用崩溃。我们已经有了修复程序,但我们需要发布一个新版本的 React Native 来应用它。

2. 更新 App 代码以使用新命令

现在你可以在应用中使用该命令了。

打开 App.tsx 文件并按如下修改:

App.tsx
import React from 'react';
-import {Alert, StyleSheet, View} from 'react-native';
-import WebView from '../specs/WebViewNativeComponent';
+import {Alert, StyleSheet, Pressable, Text, View} from 'react-native';
+import WebView, {Commands} from '../specs/WebViewNativeComponent';

function App(): React.JSX.Element {
+ const webViewRef = React.useRef<React.ElementRef<typeof View> | null>(null);
+
+ const refresh = () => {
+ if (webViewRef.current) {
+ Commands.reload(webViewRef.current);
+ }
+ };

return (
<View style={styles.container}>
<WebView
+ ref={webViewRef}
sourceURL="https://react.dev/"
style={styles.webview}
onScriptLoaded={() => {
Alert.alert('Page Loaded');
}}
/>
+ <View style={styles.tabbar}>
+ <Pressable onPress={refresh} style={styles.button}>
+ {({pressed}) => (
+ !pressed ? <Text style={styles.buttonText}>Refresh</Text> : <Text style={styles.buttonTextPressed}>Refresh</Text>) }
+ </Pressable>
+ </View>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
alignContent: 'center',
},
webview: {
width: '100%',
- height: '100%',
+ height: '90%',
},
+ tabbar: {
+ flex: 1,
+ backgroundColor: 'gray',
+ width: '100%',
+ alignItems: 'center',
+ alignContent: 'center',
+ },
+ button: {
+ margin: 10,
+ },
+ buttonText: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: '#00D6FF',
+ width: '100%',
+ },
+ buttonTextPressed: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: '#00D6FF77',
+ width: '100%',
+ },
});

export default App;

这里的相关更改如下:

  1. 从规范文件导入 Commands 常量。Command 是一个对象,让我们可以调用原生中的方法。
  2. 使用 useRef 声明对 WebView 自定义原生组件的引用。你需要将此引用传递给原生命令。
  3. 实现 refresh 函数。此函数检查 WebView 的引用是否不为 null,如果不为 null,则调用命令。
  4. 添加一个可按压元素 (Pressable) 以便用户在点击按钮时调用命令。

剩余的更改是常规的 React 更改,用于添加 Pressable 并样式化视图使其看起来更好。

3. 重新运行 Codegen

现在规范已更新且代码已准备好使用命令,是时候实现原生代码了。但是,在深入编写原生代码之前,你必须重新运行 codegen,以便它生成原生代码所需的新类型。

Codegen 通过 generateCodegenArtifactsFromSchema Gradle 任务执行:

cd android
./gradlew generateCodegenArtifactsFromSchema

BUILD SUCCESSFUL in 837ms
14 actionable tasks: 3 executed, 11 up-to-date

这是在构建 Android 应用程序时自动运行的。

4. 实现原生代码

现在是时候实现原生更改了,这将使你的 JS 能够直接调用原生视图上的方法。

为了让你的视图响应原生命令,你只需要修改 ReactWebViewManager。

如果你现在尝试构建,构建将会失败,因为当前的 ReactWebViewManager 没有实现新的 reload 方法。 为了修复构建错误,让我们修改 ReactWebViewManager 来实现它。

ReactWebViewManager.java

//...
@ReactProp(name = "sourceUrl")
@Override
public void setSourceURL(ReactWebView view, String sourceURL) {
if (sourceURL == null) {
view.emitOnScriptLoaded(ReactWebView.OnScriptLoadedEventResult.error);
return;
}
view.loadUrl(sourceURL, new HashMap<>());
}

+ @Override
+ public void reload(ReactWebView view) {
+ view.reload();
+ }

public static final String REACT_CLASS = "CustomWebView";
//...

在这种情况下,直接调用 view.reload() 方法就足够了,因为我们的 ReactWebView 继承自 Android 的 WebView,它直接有一个可用的 reload 方法。如果你正在实现一个自定义函数,而该函数在你的自定义视图中不可用,你可能还必须在由 React Native 的 ViewManager 管理的 Android 视图中实现所需的方法。

5. 运行你的应用

最后,你可以使用通常的命令运行你的应用。一旦应用运行,你可以点击刷新按钮查看页面重新加载。

Android
iOS