ネイティブとReact Native間の通信
既存アプリとの統合ガイドとネイティブUIコンポーネントガイドでは、React Nativeをネイティブコンポーネントに埋め込む方法と、その逆の方法について学びました。ネイティブコンポーネントとReact Nativeコンポーネントを混ぜ合わせると、最終的にこれら2つの世界間で通信する必要が生じます。そのためのいくつかの方法は、他のガイドですでに述べられています。この記事では、利用可能なテクニックをまとめています。
はじめに
React NativeはReactにインスパイアされているため、情報フローの基本的な考え方は似ています。Reactのフローは一方通行です。コンポーネントの階層を維持し、各コンポーネントは親とその内部状態にのみ依存します。これはプロパティを使用して行います。データは親から子へトップダウン方式で渡されます。祖先コンポーネントが子孫の状態に依存する場合、子孫が祖先を更新するために使用するコールバックを渡す必要があります。
同じ概念がReact Nativeにも当てはまります。アプリケーションをフレームワーク内で純粋に構築している限り、プロパティとコールバックでアプリを駆動できます。しかし、React Nativeとネイティブコンポーネントを混ぜ合わせる場合、それらの間で情報を渡すことができる特定のクロス言語メカニズムが必要です。
プロパティ
プロパティは、コンポーネント間のクロス通信の最も簡単な方法です。したがって、ネイティブからReact Nativeへ、そしてReact Nativeからネイティブへプロパティを渡す方法が必要です。
ネイティブからReact Nativeへのプロパティの受け渡し
メインアクティビティでReactActivityDelegate
のカスタム実装を提供することで、React Nativeアプリにプロパティを渡すことができます。この実装は、目的のプロパティを持つBundle
を返すようにgetLaunchOptions
をオーバーライドする必要があります。
- Java
- Kotlin
public class MainActivity extends ReactActivity {
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected Bundle getLaunchOptions() {
Bundle initialProperties = new Bundle();
ArrayList<String> imageList = new ArrayList<String>(Arrays.asList(
"https://dummyimage.com/600x400/ffffff/000000.png",
"https://dummyimage.com/600x400/000000/ffffff.png"
));
initialProperties.putStringArrayList("images", imageList);
return initialProperties;
}
};
}
}
class MainActivity : ReactActivity() {
override fun createReactActivityDelegate(): ReactActivityDelegate {
return object : ReactActivityDelegate(this, mainComponentName) {
override fun getLaunchOptions(): Bundle {
val imageList = arrayListOf("https://dummyimage.com/600x400/ffffff/000000.png", "https://dummyimage.com/600x400/000000/ffffff.png")
val initialProperties = Bundle().apply { putStringArrayList("images", imageList) }
return initialProperties
}
}
}
}
import React from 'react';
import {View, Image} from 'react-native';
export default class ImageBrowserApp extends React.Component {
renderImage(imgURI) {
return <Image source={{uri: imgURI}} />;
}
render() {
return <View>{this.props.images.map(this.renderImage)}</View>;
}
}
ReactRootView
は、読み書き可能なプロパティappProperties
を提供します。appProperties
が設定された後、React Nativeアプリは新しいプロパティで再レンダリングされます。更新は、新しい更新されたプロパティが以前のものと異なる場合にのみ実行されます。
- Java
- Kotlin
Bundle updatedProps = mReactRootView.getAppProperties();
ArrayList<String> imageList = new ArrayList<String>(Arrays.asList(
"https://dummyimage.com/600x400/ff0000/000000.png",
"https://dummyimage.com/600x400/ffffff/ff0000.png"
));
updatedProps.putStringArrayList("images", imageList);
mReactRootView.setAppProperties(updatedProps);
var updatedProps: Bundle = reactRootView.getAppProperties()
var imageList = arrayListOf("https://dummyimage.com/600x400/ff0000/000000.png", "https://dummyimage.com/600x400/ffffff/ff0000.png")
プロパティはいつでも更新できます。ただし、更新はメインスレッドで実行する必要があります。ゲッターはどのスレッドでも使用できます。
一度にいくつかのプロパティのみを更新する方法はありません。代わりに、独自のラッパーに組み込むことをお勧めします。
注: 現在、トップレベルのRNコンポーネントのJS関数
componentWillUpdateProps
は、プロパティ更新後に呼び出されません。ただし、componentDidMount
関数で新しいプロパティにアクセスできます。
React Nativeからネイティブへのプロパティの受け渡し
ネイティブコンポーネントのプロパティを公開する問題は、この記事で詳しく説明されています。簡単に言うと、JavaScriptに反映されるプロパティは、@ReactProp
でアノテーションされたセッターメソッドとして公開し、コンポーネントが通常のReact NativeコンポーネントであるかのようにReact Nativeで使用する必要があります。
プロパティの制限
クロス言語プロパティの主な欠点は、コールバックをサポートしていないことです。これにより、ボトムアップのデータバインディングを処理できるようになります。JSアクションの結果としてネイティブの親ビューから削除したい小さなRNビューがあると想像してみてください。情報はボトムアップで行く必要があるため、propsではそれを行う方法はありません。
クロス言語コールバックのフレーバーはありますが (ここに説明されています)、これらのコールバックは常に必要なものではありません。主な問題は、それらがプロパティとして渡されることを意図していないことです。むしろ、このメカニズムは、JSからネイティブアクションをトリガーし、そのアクションの結果をJSで処理することを可能にします。
クロス言語インタラクションのその他の方法 (イベントとネイティブモジュール)
前の章で述べたように、プロパティの使用にはいくつかの制限があります。プロパティだけではアプリのロジックを駆動するのに十分でない場合があり、より柔軟なソリューションが必要です。この章では、React Nativeで利用できる他の通信手法について説明します。これらは、内部通信 (RNのJSとネイティブレイヤー間) と外部通信 (RNとアプリの「純粋なネイティブ」部分間) の両方に使用できます。
React Nativeを使用すると、クロス言語関数呼び出しを実行できます。JSからカスタムネイティブコードを実行したり、その逆も可能です。残念ながら、作業している側によって、同じ目標を異なる方法で達成します。ネイティブの場合、イベントメカニズムを使用してJSでハンドラー関数の実行をスケジュールしますが、React Nativeの場合、ネイティブモジュールによってエクスポートされたメソッドを直接呼び出します。
ネイティブからReact Native関数を呼び出す (イベント)
イベントはこの記事で詳しく説明されています。イベントは別のスレッドで処理されるため、実行時間に関する保証がないことに注意してください。
イベントは強力です。なぜなら、参照なしにReact Nativeコンポーネントを変更できるからです。しかし、イベントを使用する際に陥りがちな落とし穴がいくつかあります。
- イベントはどこからでも送信できるため、プロジェクトにスパゲッティのような依存関係を導入する可能性があります。
- イベントは名前空間を共有するため、名前の衝突が発生する可能性があります。衝突は静的に検出されないため、デバッグが困難です。
- 同じReact Nativeコンポーネントの複数のインスタンスを使用し、イベントの観点からそれらを区別したい場合、おそらく識別子を導入し、それらをイベントと一緒に渡す必要があります (ネイティブビューの
reactTag
を識別子として使用できます)。
React Nativeからネイティブ関数を呼び出す (ネイティブモジュール)
ネイティブモジュールは、JSで利用可能なJava/Kotlinクラスです。通常、各モジュールのインスタンスはJSブリッジごとに1つ作成されます。任意の関数と定数をReact Nativeにエクスポートできます。これらはこの記事で詳しく説明されています。
警告: すべてのネイティブモジュールは同じ名前空間を共有します。新しいモジュールを作成する際は、名前の衝突に注意してください。