Skip to main content

Module events

Event emitter declaration

To make it possible for the module to emit events, JS spec needs to declare 2 methods:

src/NativeMyAwesomeModule.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

// ...

export interface Spec extends TurboModule {
addListener(eventName: string): void
removeListeners(count: number): void
}

export default TurboModuleRegistry.getEnforcing<Spec>('MyAwesomeModule');

Event emitter implementation in native code

ios/NativeMyAwesomeModule.h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface NativeMyAwesomeModule : RCTEventEmitter<RCTBridgeModule>

@end
ios/NativeMyAwesomeModule.mm
#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.

Event emitter implementation in JS code

src/MyAwesomeHook.ts
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.