メインコンテンツへスキップ

ネイティブコンポーネントでネイティブ関数を呼び出す

ベースガイドでは、新しいNative Componentを作成する方法、JS側からネイティブ側にプロパティを渡す方法、ネイティブ側からJSにイベントを送信する方法について説明しました。

カスタムコンポーネントは、ネイティブコードに実装された一部の関数を命令的に呼び出して、ウェブページのプログラムによる再読み込みなどのより高度な機能を実現することもできます。

このガイドでは、新しい概念であるNative Commandsを使用してこれを実現する方法を学びます。

このガイドはNative Componentsガイドから始まり、それに精通していること、および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. codegenNativeCommands関数をreact-nativeからインポートします。これにより、codegenはNativeCommandsのコードを生成するように指示されます。
  2. ネイティブで呼び出したいメソッドを含むインターフェースを定義します。すべてのNative Commandsは、最初のパラメータの型がReact.ElementRefである必要があります。
  3. サポートされているコマンドのリストを渡して、codegenNativeCommandsの呼び出しの結果であるCommands変数をエクスポートします。
警告

TypeScriptでは、React.ElementRefは非推奨です。正しい型は実際にはReact.ComponentRefです。しかし、Codegenのバグにより、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.dokyumento.jp/"
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を追加します。

残りの変更は、Pressableを追加し、見栄えを良くするためにビューをスタイルするための通常のReactの変更です。

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がネイティブビューでメソッドを直接呼び出せるようにするネイティブ変更を実装する番です。

ビューがNative Commandに応答できるようにするには、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";
//...

この場合、ReactWebViewはAndroidのWebViewを継承しており、reloadメソッドが直接利用できるため、view.reload()メソッドを直接呼び出すだけで十分です。カスタムビューで利用できないカスタム関数を実装している場合は、React NativeのViewManagerによって管理されるAndroidのViewにも必要なメソッドを実装する必要があるかもしれません。

5. アプリを実行する

最後に、通常のコマンドでアプリを実行できます。アプリが実行されたら、更新ボタンをタップしてページが再読み込みされるのを確認できます。

Android
iOS