ネイティブモジュールでのイベントの発行
場合によっては、プラットフォーム層のイベントをリッスンし、それらをJavaScript層に発行して、アプリケーションがそのようなネイティブイベントに反応できるようにしたいネイティブモジュールが必要になることがあります。また、UIを更新するためにイベントを発行できる、長時間実行される操作がある場合もあります。
どちらも、ネイティブモジュールからイベントを発行するのに適したユースケースです。このガイドでは、その方法について説明します。
ストレージに新しいキーが追加されたときにイベントを発行する
この例では、新しいキーがストレージに追加されたときにイベントを発行する方法を学びます。キーの値が変更されてもイベントは発行されませんが、新しいキーが追加されると発行されます。
このガイドは、ネイティブモジュールガイドから始まります。このガイドに入る前に、そのガイドに精通し、可能であればガイドの例を実装してください。
ステップ1: NativeLocalStorageのスペックを更新する
最初のステップは、モジュールがイベントを発行できることをReact Nativeに認識させるために、NativeLocalStorageのスペックを更新することです。
- TypeScript
- Flow
NativeLocalStorage.tsファイルを開き、次のように更新します。
+import type {TurboModule, CodegenTypes} from 'react-native';
import {TurboModuleRegistry} from 'react-native';
+export type KeyValuePair = {
+ key: string,
+ value: string,
+}
export interface Spec extends TurboModule {
setItem(value: string, key: string): void;
getItem(key: string): string | null;
removeItem(key: string): void;
clear(): void;
+ readonly onKeyAdded: CodegenTypes.EventEmitter<KeyValuePair>;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'NativeLocalStorage',
);
NativeLocalStorage.jsファイルを開き、次のように更新します。
// @flow
+import type {TurboModule, CodegenTypes} from 'react-native';
import {TurboModule, TurboModuleRegistry} from 'react-native';
+export type KeyValuePair = {
+ key: string,
+ value: string,
+}
export interface Spec extends TurboModule {
setItem(value: string, key: string): void;
getItem(key: string): ?string;
removeItem(key: string): void;
clear(): void;
+ onKeyAdded: CodegenTypes.EventEmitter<KeyValuePair>
}
export default (TurboModuleRegistry.get<Spec>(
'NativeLocalStorage'
): ?Spec);
import typeステートメントを使用すると、react-nativeからCodegenTypesをインポートしており、これにはEventEmitter型が含まれます。これにより、onKeyAddedプロパティをCodegenTypes.EventEmitter<KeyValuePair>を使用して定義でき、イベントがKeyValuePair型のペイロードを発行することを指定できます。
イベントが発行されると、string型のパラメータを受け取ることが期待されます。
ステップ2: コードジェネレーションを生成する
ネイティブモジュールのスペックを更新したので、コードジェネレーションを再実行して、ネイティブコードにアーティファクトを生成する必要があります。
これは、ネイティブモジュールガイドで説明されているのと同じプロセスです。
- Android
- iOS
Codegenは `generateCodegenArtifactsFromSchema` Gradleタスクを通じて実行されます。
cd android
./gradlew generateCodegenArtifactsFromSchema
BUILD SUCCESSFUL in 837ms
14 actionable tasks: 3 executed, 11 up-to-date
これはAndroidアプリケーションをビルドする際に自動的に実行されます。
Codegenは、CocoaPodsによって生成されたプロジェクトに自動的に追加されるスクリプトフェーズの一部として実行されます。
cd ios
bundle install
bundle exec pod install
出力は次のようになります。
...
Framework build type is static library
[Codegen] Adding script_phases to ReactCodegen.
[Codegen] Generating ./build/generated/ios/ReactCodegen.podspec.json
[Codegen] Analyzing /Users/me/src/TurboModuleExample/package.json
[Codegen] Searching for Codegen-enabled libraries in the app.
[Codegen] Found TurboModuleExample
[Codegen] Searching for Codegen-enabled libraries in the project dependencies.
[Codegen] Found react-native
...
ステップ3: アプリのコードを更新する
次に、新しいイベントを処理するためにアプリのコードを更新します。
App.tsxファイルを開き、次のように変更します。
import React from 'react';
import {
+ Alert,
+ EventSubscription,
SafeAreaView,
StyleSheet,
Text,
TextInput,
Button,
} from 'react-native';
import NativeLocalStorage from './specs/NativeLocalStorage';
const EMPTY = '<empty>';
function App(): React.JSX.Element {
const [value, setValue] = React.useState<string | null>(null);
+ const [key, setKey] = React.useState<string | null>(null);
+ const listenerSubscription = React.useRef<null | EventSubscription>(null);
+ React.useEffect(() => {
+ listenerSubscription.current = NativeLocalStorage?.onKeyAdded((pair) => Alert.alert(`New key added: ${pair.key} with value: ${pair.value}`));
+ return () => {
+ listenerSubscription.current?.remove();
+ listenerSubscription.current = null;
+ }
+ }, [])
const [editingValue, setEditingValue] = React.useState<
string | null
>(null);
- React.useEffect(() => {
- const storedValue = NativeLocalStorage?.getItem('myKey');
- setValue(storedValue ?? '');
- }, []);
function saveValue() {
+ if (key == null) {
+ Alert.alert('Please enter a key');
+ return;
+ }
NativeLocalStorage?.setItem(editingValue ?? EMPTY, key);
setValue(editingValue);
}
function clearAll() {
NativeLocalStorage?.clear();
setValue('');
}
function deleteValue() {
+ if (key == null) {
+ Alert.alert('Please enter a key');
+ return;
+ }
NativeLocalStorage?.removeItem(key);
setValue('');
}
+ function retrieveValue() {
+ if (key == null) {
+ Alert.alert('Please enter a key');
+ return;
+ }
+ const val = NativeLocalStorage?.getItem(key);
+ setValue(val);
+ }
return (
<SafeAreaView style={{flex: 1}}>
<Text style={styles.text}>
Current stored value is: {value ?? 'No Value'}
</Text>
+ <Text>Key:</Text>
+ <TextInput
+ placeholder="Enter the key you want to store"
+ style={styles.textInput}
+ onChangeText={setKey}
+ />
+ <Text>Value:</Text>
<TextInput
placeholder="Enter the text you want to store"
style={styles.textInput}
onChangeText={setEditingValue}
/>
<Button title="Save" onPress={saveValue} />
+ <Button title="Retrieve" onPress={retrieveValue} />
<Button title="Delete" onPress={deleteValue} />
<Button title="Clear" onPress={clearAll} />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
text: {
margin: 10,
fontSize: 20,
},
textInput: {
margin: 10,
height: 40,
borderColor: 'black',
borderWidth: 1,
paddingLeft: 5,
paddingRight: 5,
borderRadius: 5,
},
});
export default App;
いくつか関連する変更点があります。
EventSubscriptionを処理するために、react-nativeからEventSubscription型をインポートする必要があります。useRefを使用して、EventSubscription参照を追跡する必要があります。useEffectフックを使用してリスナーを登録します。onKeyAdded関数は、関数パラメータとしてKeyValuePair型のオブジェクトを持つコールバックを受け取ります。onKeyAddedに追加されたコールバックは、イベントがNativeからJSに発行されるたびに実行されます。useEffectのクリーンアップ関数では、イベントサブスクリプションをremoveし、refをnullに設定します。
残りの変更は、この新機能のためにアプリを改善するための通常のReactの変更です。
ステップ4: ネイティブコードを書く
すべてが準備できたので、ネイティブプラットフォームコードを書き始めましょう。
- Android
- iOS
ネイティブモジュールガイドで説明されているiOSのガイドに従っていると仮定すると、残りの作業は、アプリにイベントを発行するコードを接続することです。
そのためには、
NativeLocalStorage.ktファイルを開き、- 次のように変更します。
package com.nativelocalstorage
import android.content.Context
import android.content.SharedPreferences
import com.nativelocalstorage.NativeLocalStorageSpec
+import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactApplicationContext
+import com.facebook.react.bridge.WritableMap
class NativeLocalStorageModule(reactContext: ReactApplicationContext) : NativeLocalStorageSpec(reactContext) {
override fun getName() = NAME
override fun setItem(value: String, key: String) {
+ var shouldEmit = false
+ if (getItem(key) != null) {
+ shouldEmit = true
+ }
val sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
val editor = sharedPref.edit()
editor.putString(key, value)
editor.apply()
+ if (shouldEmit == true) {
+ val eventData = Arguments.createMap().apply {
+ putString("key", key)
+ putString("value", value)
+ }
+ emitOnKeyAdded(eventData)
+ }
}
override fun getItem(key: String): String? {
val sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
val username = sharedPref.getString(key, null)
return username.toString()
}
まず、NativeからJSに送信する必要があるイベントデータを作成するために使用するいくつかの型をインポートする必要があります。これらのインポートは次のとおりです。
import com.facebook.react.bridge.Argumentsimport com.facebook.react.bridge.WritableMap
次に、実際にイベントをJSに発行するロジックを実装する必要があります。スペックで定義されているKeyValuePairのような複雑な型の場合、CodegenはReadableMapをパラメータとして期待する関数を生成します。Arguments.createMap()ファクトリメソッドを使用してReadableMapを作成し、apply関数を使用してマップを埋めることができます。マップで使用しているキーが、JSのスペック型で定義されているプロパティと同じであることを確認するのは、あなたの責任です。
ネイティブモジュールガイドで説明されているiOSのガイドに従っていると仮定すると、残りの作業は、アプリにイベントを発行するコードを接続することです。
そのためには、
RCTNativeLocalStorage.hファイルを開きます。- 基本クラスを
NSObjectからNativeLocalStorageSpecBaseに変更します。
#import <Foundation/Foundation.h>
#import <NativeLocalStorageSpec/NativeLocalStorageSpec.h>
NS_ASSUME_NONNULL_BEGIN
-@interface RCTNativeLocalStorage : NSObject <NativeLocalStorageSpec>
+@interface RCTNativeLocalStorage : NativeLocalStorageSpecBase <NativeLocalStorageSpec>
@end
NS_ASSUME_NONNULL_END
RCTNativeLocalStorage.mmファイルを開きます。- 必要に応じてイベントを発行するように変更します。例えば、
- (void)setItem:(NSString *)value key:(NSString *)key {
+ BOOL shouldEmitEvent = NO;
+ if (![self getItem:key]) {
+ shouldEmitEvent = YES;
+ }
[self.localStorage setObject:value forKey:key];
+ if (shouldEmitEvent) {
+ [self emitOnKeyAdded:@{@"key": key, @"value": value}];
+ }
}
NativeLocalStorageSpecBaseは、emitOnKeyAddedメソッドとその基本的な実装およびボイラープレートを提供する基本クラスです。このクラスのおかげで、イベントをJSに送信するために必要なObjective-CとJSI間のすべての変換を処理する必要がありません。
スペックで定義されているKeyValuePairのような複雑な型の場合、Codegenはネイティブ側で埋めることができる汎用辞書を生成します。辞書で使用しているキーが、JSのスペック型で定義されているプロパティと同じであることを確認するのは、あなたの責任です。
ステップ5: アプリを実行する
これでアプリを実行すると、次の動作が表示されるはずです。
![]() | ![]() |

