跳到主要内容

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

基础指南中,您已经了解了如何创建一个新的原生组件,如何将属性从 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 中的一个错误,使用ComponentRef将导致应用程序崩溃。我们已经有了修复方案,但需要发布新版本的 React Native 才能应用它。

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

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

打开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://reactjs.ac.cn/"
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 通过 generateCodegenArtifactsFromSchema Gradle 任务执行

bash
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. 运行您的应用程序

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

安卓
iOS