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

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

新しいネイティブコンポーネントを作成するための基本ガイドでは、新しいコンポーネントを作成する方法、JS側からネイティブ側へプロパティを渡す方法、ネイティブ側からJSへイベントを発行する方法について学びました。

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

このガイドでは、新しい概念である「ネイティブコマンド」を使用してこれを実現する方法を学びます。

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

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://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. Specファイルから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からネイティブビューのメソッドを直接呼び出せるようにするネイティブの変更を実装する時が来ました。

ビューがネイティブコマンドに応答できるようにするには、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