Module events (old arch)
You may notice, that JS specs contain codegen-related methods, classes, types, etc. to make things more future-proof.
That's because:
- those elements are available since RN older versions (even from v0.65)
- those elements are falling back to "old architecture" implementation (e.g. codegenNativeComponent)
- it introduces type safety for exposed native parts on JS side
- it's much easier to keep single specification on JS side - when old arch will be dropped, there'll be no need to change anything on JS side
So to make it easier, let's use them, to get you more familiar with it 👍
Event emitter declaration
To make it possible for the module to emit events, JS spec needs to declare 2 methods:
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
// ...
export interface Spec extends TurboModule {
addListener(eventName: string): void
removeListeners(count: number): void
}
// TurboModuleRegistry uses NativeModules['MyAwesomeModule'] on old arch
export default TurboModuleRegistry.getEnforcing<Spec>('MyAwesomeModule');
Event emitter implementation in native code
- iOS
- Android
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface NativeMyAwesomeModule : RCTEventEmitter<RCTBridgeModule>
@end
#import "NativeMyAwesomeModule.h"
@implementation NativeMyAwesomeModule {
BOOL hasListeners;
}
RCT_EXPORT_MODULE(NativeMyAwesomeModule)
- (NSArray<NSString *> *)supportedEvents
{
return @[@"decentEvent", @"notGreatEvent"];
}
- (void)startObserving
{
hasListeners = YES;
}
- (void)stopObserving
{
hasListeners = NO;
}
- (void)onSomeNativeEvent:(NSString *)someNativeValue
{
if (hasListeners) {
[self sendEventWithName:@"decentEvent" body:@{ @"value": someNativeValue }];
}
}
@end
To enhance iOS native module with event emitting feature, it has to extend RCTEventEmitter
abstract class.
Then in the implementation part, module has to override supportedEvents
getter which declares names for events emitted by this module.
It has to also override startObserving
and stopObserving
methods, which are called when first and last listeners are added/removed.
To emit event, the module should call sendEventWithName:body:
method, which is inherited from RCTEventEmitter
class.
It takes event name and an object payload (NSDictionary type), which will be sent to JS listener.
- Kotlin
- Java
import com.facebook.react.bridge.ReactMethod
// ...
@ReactMethod
fun addListener(eventName: String?) = Unit
@ReactMethod
fun removeListeners(count: Double) = Unit
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactContext
import com.facebook.react.modules.core.DeviceEventManagerModule
// ...
fun onSomeNativeEvent(someNativeValue: String) {
val reactContext: ReactContext = getReactContextFromTheModule()
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("decentEvent", Arguments.createMap().apply {
putString("value", someNativeValue)
})
}
import com.facebook.react.bridge.ReactMethod;
// ...
@ReactMethod
public void addListener(String eventName) {}
@ReactMethod
public void removeListeners(double count) {}
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
// ...
private void onSomeNativeEvent(String someNativeValue) {
ReactContext reactContext = getReactContextFromTheModule();
WritableMap payload = Arguments.createMap();
payload.putString("value", someNativeValue);
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("decentEvent", payload);
}
To enhance Android native module with event emitting feature, it has to:
- declare
addListener
andremoveListeners
methods - use
RCTDeviceEventEmitter
class for events emitting
To emit event, the module should call emit
method on RCTDeviceEventEmitter
instance.
The first argument is the event name and the second one is payload object (WritableMap
type) which will be sent to JS listener.
It can be constructed with Arguments.createMap
utility function.
Event emitter implementation in JS code
import { NativeEventEmitter, Platform } from 'react-native';
import { MyAwesomeModule } from 'my-awesome-module';
const moduleEventEmitter = new NativeEventEmitter(Platform.OS === 'ios' ? MyAwesomeModule : undefined);
// ...
useEffect(() => {
const subscription = moduleEventEmitter.addListener('decentEvent', ({ value }: { value: string }) => {
console.log({ someNativeValue: value });
});
return () => {
subscription.remove();
};
}, []);
To start listening to events emitted from native side, you have to:
- create
NativeEventEmitter
instance - invoke
addListener
method on that instance, providing the event name and the listener function
When the event is emitted, all listeners for that specific event will be called with the payload value.
When the listener is not needed, it should be removed - as a cleanup, it's recommended to invoke remove
method on the object returned from addListener
method call.
It will unregister provided listener from emitted native events.