跳到主要内容
版本:0.80

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

在编写新原生组件的 基础指南 中,你已经探索了如何创建新组件,如何将属性从 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 中使用该命令了。

打开 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 View 中实现所需的方法。

5. 运行您的应用

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

Android
iOS