跳至主内容
版本:0.82
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

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

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

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

本指南将介绍如何通过新概念——Native Commands(原生命令)实现这一功能。

本指南基于原生组件指南编写,假设您已熟悉该指南并了解Codegen

1. 更新组件规范

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

Update the WebViewNativeComponent.ts as it follows:

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. 定义包含要在原生端调用方法的接口。所有Native Commands的第一个参数必须是React.ElementRef类型

  3. 导出Commands变量,该变量是调用codegenNativeCommands的结果,需传入支持的命令列表

警告

在TypeScript中,React.ElementRef已弃用。正确类型应为React.ComponentRef。但由于Codegen存在bug,使用ComponentRef会导致应用崩溃。我们已修复该问题,但需要发布新版React Native才能应用。

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

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

Open the App.tsx file and modify it as it follows:

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常量。该命令对象允许我们调用原生端的方法

  2. 使用useRef声明WebView自定义原生组件的引用。需将此引用传递给原生命令

  3. 实现refresh函数。此函数检查WebView引用是否非空,若非空则调用命令

  4. 添加可点击组件,当用户点击按钮时调用命令

其余变更是常规React变更,用于添加Pressable组件并优化视图样式

3. 重新运行Codegen

规范更新完毕且代码已准备好使用命令后,接下来需要实现原生代码。但在编写原生代码前,必须重新运行Codegen以生成原生代码所需的新类型

Codegen is executed through the generateCodegenArtifactsFromSchema Gradle task:

bash
cd android
./gradlew generateCodegenArtifactsFromSchema

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

This is automatically run when you build your Android application.

4. 实现原生代码

现在可以实施原生端变更,使JS能够直接调用原生视图的方法

To let your view respond to the Native Command, you only have to modify the ReactWebViewManager.

If you try to build right now, the build will fail, because the current ReactWebViewManager does not implement the new reload method. To fix the build error, let's modify the ReactWebViewManager to implement it.

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";
//...

In this case, it's enough to call directly the view.reload() method because our ReactWebView inherits from the Android's WebView and it has a reload method directly available. If you are implementing a custom function, that is not available in your custom view, you might also have to implement the required method in the Android's View that is managed by the React Native's ViewManager.

5. 运行应用

最后,您可以使用常规命令运行应用。应用运行后,点击刷新按钮即可看到页面重新加载

Android
iOS