ネイティブモジュールでのイベントの発行
状況によっては、プラットフォーム層で特定のイベントをリッスンし、それらをJavaScript層に発行して、アプリケーションがそのようなネイティブイベントに反応できるようにしたい場合があります。また、実行時間の長い操作がイベントを発行し、それが発生したときにUIを更新できる場合もあります。
どちらも、ネイティブモジュールからイベントを発行するための良いユースケースです。このガイドでは、その方法を学びます。
ストレージに新しいキーが追加されたときにイベントを発行する
この例では、ストレージに新しいキーが追加されたときにイベントを発行する方法を学びます。キーの値を変更してもイベントは発行されませんが、新しいキーを追加するとイベントが発行されます。
このガイドはネイティブモジュールガイドから始まります。このガイドに入る前に、そのガイドに慣れておき、可能であればそのガイドの例を実装してください。
ステップ1:NativeLocalStorageの仕様を更新する
最初のステップは、NativeLocalStorage
の仕様を更新し、モジュールがイベントを発行できることをReact Nativeに認識させることです。
- 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:Codegenを生成する
ネイティブモジュールの仕様を更新したので、ネイティブコードで成果物を生成するためにCodegenを再実行する必要があります。
これは、ネイティブモジュールガイドで説明されているプロセスと同じです。
- 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
型をインポートする必要があります。EventSubscription
参照を追跡するためにuseRef
を使用する必要があります。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に送信する必要があるeventDataを作成するために使用するいくつかの型をインポートする必要があります。これらのインポートは次のとおりです。
import com.facebook.react.bridge.Arguments
import 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:アプリを実行する
これでアプリを実行しようとすると、この動作が表示されるはずです。
![]() | ![]() |