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

ネイティブコンポーネント

AndroidのCheckBoxやiOSのUIButtonのようなホストコンポーネントをラップする*新しい* React Nativeコンポーネントを構築する場合は、Fabric Nativeコンポーネントを使用する必要があります。

このガイドでは、Webビューコンポーネントを実装することで、Fabric Nativeコンポーネントを構築する方法を説明します。手順は以下のとおりです。

  1. FlowまたはTypeScriptを使用してJavaScript仕様を定義します。
  2. 依存関係管理システムを設定して、提供された仕様からコードを生成し、自動的にリンクされるようにします。
  3. ネイティブコードを実装します。
  4. アプリでコンポーネントを使用します。

コンポーネントを使用するには、プレーンテンプレートで生成されたアプリケーションが必要です。

npx @react-native-community/cli@latest init Demo --install-pods false

WebViewコンポーネントの作成

このガイドでは、Webビューコンポーネントを作成する方法を説明します。AndroidのWebViewコンポーネントとiOSのWKWebViewコンポーネントを使用してコンポーネントを作成します。

まず、コンポーネントのコードを格納するフォルダ構造を作成しましょう。

mkdir -p Demo/{specs,android/app/src/main/java/com/webview}

これにより、作業を行う次のレイアウトが得られます。

Demo
├── android/app/src/main/java/com/webview
└── ios
└── spec
  • android/app/src/main/java/com/webviewフォルダは、Androidコードを格納するフォルダです。
  • iosフォルダは、iOSコードを格納するフォルダです。
  • specフォルダは、コード生成の仕様ファイルを格納するフォルダです。

1. コード生成の仕様を定義する

仕様は、TypeScriptまたはFlowで定義する必要があります(詳細については、コード生成のドキュメントを参照してください)。これは、コード生成によって、プラットフォームコードをReactが実行されるJavaScriptランタイムに接続するためのC++、Objective-C++、およびJavaを生成するために使用されます。

仕様ファイルは、コード生成で機能するように、<MODULE_NAME>NativeComponent.{ts|js}という名前である必要があります。接尾辞NativeComponentは単なる規則ではなく、実際にコード生成によって仕様ファイルを検出するために使用されます。

WebViewコンポーネントには、この仕様を使用します。

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';

type WebViewScriptLoadedEvent = {
result: 'success' | 'error';
};

export interface NativeProps extends ViewProps {
sourceURL?: string;
onScriptLoaded?: BubblingEventHandler<WebViewScriptLoadedEvent> | null;
}

export default codegenNativeComponent<NativeProps>(
'CustomWebView',
) as HostComponent<NativeProps>;

この仕様は、インポートを除いて、3つの主要部分で構成されています。

  • WebViewScriptLoadedEventは、イベントがネイティブからJavaScriptに渡す必要があるデータのサポートデータ型です。
  • NativePropsは、コンポーネントに設定できるプロパティの定義です。
  • codegenNativeComponentステートメントは、カスタムコンポーネントのコードをコード生成することを可能にし、ネイティブ実装と一致するために使用されるコンポーネントの名前を定義します。

ネイティブモジュールと同様に、specs/ディレクトリに複数の仕様ファイルを含めることができます。使用できる型と、これらがマップされるプラットフォーム型の詳細については、付録を参照してください。

2. コード生成を実行するように構成する

この仕様は、React Nativeのコード生成ツールによって、プラットフォーム固有のインターフェースとボイラープレートを生成するために使用されます。これを行うために、コード生成は、仕様がどこにあるか、そしてそれで何をする必要があるかを知る必要があります。package.jsonを更新して、以下を含めます。

     "start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "AppSpec",
"type": "components",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.nativelocalstorage"
}
},
"dependencies": {

コード生成のすべてが配線されたので、生成されたコードにフックするようにネイティブコードを準備する必要があります。

2. ネイティブコードの構築

Reactがビューのレンダリングを要求したときに、プラットフォームが適切なネイティブビューを作成して画面にレンダリングできるように、ネイティブプラットフォームコードを記述します。

AndroidとiOSの両方のプラットフォームで作業する必要があります。

注記

このガイドでは、新しいアーキテクチャでのみ機能するネイティブコンポーネントの作成方法を示しています。新しいアーキテクチャとレガシーアーキテクチャの両方をサポートする必要がある場合は、下位互換性ガイドを参照してください。

Webビューをレンダリングできるように、Androidプラットフォームコードを記述します。以下の手順に従う必要があります。

  • コード生成の実行
  • ReactWebViewのコードを記述する
  • ReactWebViewManagerのコードを記述する
  • ReactWebViewPackageのコードを記述する
  • アプリケーションにReactWebViewPackageを登録する

1. Gradleを介してコード生成を実行する

これを実行して、選択したIDEが使用できるボイラープレートを生成します。

Demo/
cd android
./gradlew generateCodegenArtifactsFromSchema

コード生成は、実装する必要があるViewManagerインターフェースと、WebビューのViewManagerデリゲートを生成します。

2. ReactWebViewを記述する

ReactWebViewは、React Nativeがカスタムコンポーネントを使用するときにレンダリングするAndroidネイティブビューをラップするコンポーネントです。

android/src/main/java/com/webviewフォルダに、このコードを含むReactWebView.javaファイルまたはReactWebView.ktファイルを作成します。

Demo/android/src/main/java/com/webview/ReactWebView.java
package com.webview;

import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.events.Event;

public class ReactWebView extends WebView {
public ReactWebView(Context context) {
super(context);
configureComponent();
}

public ReactWebView(Context context, AttributeSet attrs) {
super(context, attrs);
configureComponent();
}

public ReactWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
configureComponent();
}

private void configureComponent() {
this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
this.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
emitOnScriptLoaded(OnScriptLoadedEventResult.success);
}
});
}

public void emitOnScriptLoaded(OnScriptLoadedEventResult result) {
ReactContext reactContext = (ReactContext) context;
int surfaceId = UIManagerHelper.getSurfaceId(reactContext);
EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId());
WritableMap payload = Arguments.createMap();
payload.putString("result", result.name());

OnScriptLoadedEvent event = new OnScriptLoadedEvent(surfaceId, getId(), payload);
if (eventDispatcher != null) {
eventDispatcher.dispatchEvent(event);
}
}

public enum OnScriptLoadedEventResult {
success,
error
}

private class OnScriptLoadedEvent extends Event<OnScriptLoadedEvent> {
private final WritableMap payload;

OnScriptLoadedEvent(int surfaceId, int viewId, WritableMap payload) {
super(surfaceId, viewId);
this.payload = payload;
}

@Override
public String getEventName() {
return "onScriptLoaded";
}

@Override
public WritableMap getEventData() {
return payload;
}
}
}

ReactWebViewはAndroid WebViewを拡張するため、プラットフォームによって既に定義されているすべてのプロパティを簡単に再利用できます。

クラスは3つのAndroidコンストラクタを定義しますが、実際の実装はプライベートなconfigureComponent関数に委任します。この関数は、すべてのコンポーネント固有のプロパティの初期化を処理します。この場合、WebViewのレイアウトを設定し、WebViewの動作をカスタマイズするために使用するWebClientを定義しています。このコードでは、ReactWebViewは、WebClientonPageFinishedメソッドを実装することで、ページの読み込みが完了したときにイベントを発行します。

次に、コードはイベントを実際に発行するためのヘルパー関数を定義します。イベントを発行するには、次の手順を実行する必要があります。

  • ReactContextへの参照を取得します。
  • 表示しているビューのsurfaceIdを取得します。
  • ビューに関連付けられたeventDispatcherへの参照を取得します。
  • WritableMapオブジェクトを使用して、イベントのペイロードを作成します。
  • JavaScriptに送信する必要があるイベントオブジェクトを作成します。
  • eventDispatcher.dispatchEventを呼び出して、イベントを送信します。

ファイルの最後の部分には、イベントを送信するために必要なデータ型の定義が含まれています。

  • OnScriptLoadedイベントの考えられる結果を含むOnScriptLoadedEventResult
  • React NativeのEventクラスを拡張する必要がある実際のOnScriptLoadedEvent

3. WebViewManagerを記述する

WebViewManagerは、React Nativeランタイムとネイティブビューを接続するクラスです。

Reactがアプリから特定のコンポーネントをレンダリングする命令を受け取ると、Reactは登録されたビューマネージャーを使用してビューを作成し、必要なすべてのプロパティを渡します。

これは、ReactWebViewManagerのコードです。

Demo/android/src/main/java/com/webview/ReactWebViewManager.java
package com.webview;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.viewmanagers.CustomWebViewManagerInterface;
import com.facebook.react.viewmanagers.CustomWebViewManagerDelegate;

import java.util.HashMap;
import java.util.Map;

@ReactModule(name = ReactWebViewManager.REACT_CLASS)
class ReactWebViewManager extends SimpleViewManager<ReactWebView> implements CustomWebViewManagerInterface<ReactWebView> {
private final CustomWebViewManagerDelegate<ReactWebView, ReactWebViewManager> delegate =
new CustomWebViewManagerDelegate<>(this);

@Override
public ViewManagerDelegate<ReactWebView> getDelegate() {
return delegate;
}

@Override
public String getName() {
return REACT_CLASS;
}

@Override
public ReactWebView createViewInstance(ThemedReactContext context) {
return new ReactWebView(context);
}

@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<>());
}

public static final String REACT_CLASS = "CustomWebView";

@Override
public Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
Map<String, Object> map = new HashMap<>();
Map<String, Object> bubblingMap = new HashMap<>();
bubblingMap.put("phasedRegistrationNames", new HashMap<String, String>() {{
put("bubbled", "onScriptLoaded");
put("captured", "onScriptLoadedCapture");
}});
map.put("onScriptLoaded", bubblingMap);
return map;
}
}

ReactWebViewManager は、React の SimpleViewManager クラスを拡張し、Codegen によって生成された CustomWebViewManagerInterface を実装します。

これは、Codegen によって生成された別の要素である CustomWebViewManagerDelegate への参照を保持します。

次に、getName 関数をオーバーライドします。この関数は、仕様の codegenNativeComponent 関数呼び出しで使用されるのと同じ名前を返す必要があります。

createViewInstance 関数は、新しい ReactWebView をインスタンス化する役割を担います。

次に、ViewManager は、React のコンポーネントのすべての props がネイティブビューをどのように更新するかを定義する必要があります。この例では、React が WebView に設定する sourceURL プロパティをどのように処理するかを決定する必要があります。

最後に、コンポーネントがイベントを発行できる場合、バブリングイベントの場合は getExportedCustomBubblingEventTypeConstants を、直接イベントの場合は getExportedCustomDirectEventTypeConstants をオーバーライドして、イベント名をマッピングする必要があります。

4. ReactWebViewPackage を記述する

ネイティブモジュールと同様に、ネイティブコンポーネントも ReactPackage クラスを実装する必要があります。これは、React Native ランタイムにコンポーネントを登録するために使用できるオブジェクトです。

これは ReactWebViewPackage のコードです。

Demo/android/src/main/java/com/webview/ReactWebViewPackage.java
package com.webview;

import com.facebook.react.TurboReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.uimanager.ViewManager;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ReactWebViewPackage extends TurboReactPackage {
@Override
public List<ViewManager<?, ?>> createViewManagers(ReactApplicationContext reactContext) {
return Collections.singletonList(new ReactWebViewManager(reactContext));
}

@Override
public NativeModule getModule(String s, ReactApplicationContext reactApplicationContext) {
if (ReactWebViewManager.REACT_CLASS.equals(s)) {
return new ReactWebViewManager(reactApplicationContext);
}
return null;
}

@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> get() {
Map<String, ReactModuleInfo> map = new HashMap<>();
map.put(ReactWebViewManager.REACT_CLASS, new ReactModuleInfo(
ReactWebViewManager.REACT_CLASS, // name
ReactWebViewManager.REACT_CLASS, // className
false, // canOverrideExistingModule
false, // needsEagerInit
false, // isCxxModule
true // isTurboModule
));
return map;
}
};
}
}

ReactWebViewPackageTurboReactPackage を拡張し、コンポーネントを適切に登録するために必要なすべてのメソッドを実装します。

  • createViewManagers メソッドは、カスタムビューを管理する ViewManager を作成するファクトリメソッドです。
  • getModule メソッドは、React Native がレンダリングする必要がある View に応じて、適切な ViewManager を返します。
  • getReactModuleInfoProvider は、ランタイムにモジュールを登録するときに必要なすべての情報を提供します。

5. アプリケーションに ReactWebViewPackage を登録する

最後に、アプリケーションに ReactWebViewPackage を登録する必要があります。そのためには、MainApplication ファイルを変更し、getPackages 関数によって返されるパッケージのリストに ReactWebViewPackage を追加します。

Demo/app/src/main/java/com/demo/MainApplication.kt
package com.demo

import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader
import com.webview.ReactWebViewPackage

class MainApplication : Application(), ReactApplication {

override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(ReactWebViewPackage())
}

override fun getJSMainModuleName(): String = "index"

override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG

override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}

override val reactHost: ReactHost
get() = getDefaultReactHost(applicationContext, reactNativeHost)

override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
load()
}
}
}

3. ネイティブコンポーネントを使用する

最後に、新しいコンポーネントをアプリで使用できます。生成された `App.tsx` を次のように更新します。

Demo/App.tsx
import React from 'react';
import {Alert, StyleSheet, View} from 'react-native';
import WebView from './specs/WebViewNativeComponent';

function App(): React.JSX.Element {
return (
<View style={styles.container}>
<WebView
sourceURL="https://react.dokyumento.jp/"
style={styles.webview}
onScriptLoaded={() => {
Alert.alert('Page Loaded');
}}
/>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
alignContent: 'center',
},
webview: {
width: '100%',
height: '100%',
},
});

export default App;

このコードは、作成した新しい `WebView` コンポーネントを使用して `react.dev` Web サイトを読み込むアプリを作成します。

また、アプリは Web ページが読み込まれたときにアラートを表示します。

5. WebView コンポーネントを使用してアプリを実行する

yarn run android
AndroidiOS