The code is here
Many times we need to send events from native to JS. For example, a calendar event mentioned in the official document. You have scheduled a meeting or an event, and then it will happen on a specified date. Or turn off the contribution bike, and Bluetooth receives the signal that the lock is successful. Or for apps like geofences, when you enter/leave a geofence, you need to send events natively to JS.
First is a simple example
Calling a native method to set the native time of a delayed trigger is similar to calling the native setTimeout
. After the time is up, an event will be sent from native to JS.
First, there will be a text box on the UI where you can enter the time, after the user enters the time and clicks the OK button. App will call the native method to execute the native setTimeout
method.
The native method called by the App is a promise method on the front end. Therefore, this method can be called async-await
An event listener will be registered in the JS part, and the JS code will be executed as soon as the native event is received. In this example, only a log is output for simplicity.
Since events are received and sent from native, a native module is essential. Know very little about this part of the students to be the venue This is the iOS , this is Android's
一点需要更新的是,现在官方推荐在实现Android原生模块的时候使用`ReactContextBaseJavaModule`。主要是出于类型安全的考虑。
Implement a native module in iOS
// header file
@interface FillingHoleModule: RCTEventEmitter<RCTBridgeModule>
@end
// implementation
#import "FillingHoleModule.h"
@implementation FillingHoleModule
RCT_EXPORT_MODULE(FillingHoleModule)
RCT_EXPORT_METHOD(sendEventInSeconds: (NSUInteger) seconds resolver:(RCTPromiseResolveBlock) resolve
rejecter: (RCTPromiseRejectBlock) reject) {
@try {
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * seconds);
dispatch_after(delay, dispatch_get_main_queue(), ^(void) {
[self sendEventWithName:@"FillingHole" body:@{@"filling": @"hole", @"with": @"RN"}];
});
NSLog(@"Resolved");
resolve(@"done");
} @catch (NSException *exception) {
NSLog(@"Rejected %@", exception);
reject(@"Failed", @"Cannot setup timeout", nil);
}
}
- (NSArray<NSString *> *)supportedEvents {
return @[@"FillingHole"];
}
@end
这里省略了模块头文件。
1 : In the header file, you can see that the native module inherits RCTEventEmitter
.
2 : So when implementing, you need to implement the method supportedEvents
this class. It is to add the name of the event we want to send from the original. like:
- (NSArray<NSString *> *)supportedEvents {
return @[@"FillingHole"];
}
3 : The try-catch in method sendEventInSeconds
resolve
and reject
just to realize the promise of the JS part
4 : This part is equivalent to setTimeout
:
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * seconds);
dispatch_after(delay, dispatch_get_main_queue(), ^(void) {
[self sendEventWithName:@"FillingHole" body:@{@"filling": @"hole", @"with": @"RN"}];
});
The code [self sendEventWithName:@"FillingHole" body:@{@"filling": @"hole", @"with": @"RN"}];
completes the function of sending events from native to JS.
Native modules in Android
public class FillingEventHole extends ReactContextBaseJavaModule {
FillingEventHole(ReactApplicationContext context) {
super(context);
}
@NonNull
@Override
public String getName() {
return "FillingHoleModule";
}
@ReactMethod
public void sendEventInSeconds(long seconds, Promise promise) {
Log.d("FillEventHole", "Event from native comes in" + seconds);
try {
new android.os.Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
WritableMap params = Arguments.createMap();
params.putString("filling", "hole");
params.putString("with", "RN");
FillingEventHole.this.sendEvent("FillingHole", params);
}
}, seconds * 1000);
promise.resolve("Done");
} catch (Exception e) {
promise.reject(e);
}
}
private void sendEvent(String eventName, @Nullable WritableMap params) {
this.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
}
}
Note : The specific native steps are a bit more than iOS. Here are all omitted, if you need to refer to the Android native module part mentioned above.
1 : sendEventInSeconds
used to receive the time information passed by JS, and start setTimeout
Android here.
2 : The try-catch part is the same as that of iOS, cooperating with resolve and reject.
3 : Android’s setTimeout
:
new android.os.Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
WritableMap params = Arguments.createMap();
params.putString("filling", "hole");
params.putString("with", "RN");
FillingEventHole.this.sendEvent("FillingHole", params);
}
}, seconds * 1000);
4 : method FillingEventHole.this.sendEvent("FillingHole", params);
call implemented in the native module in setEvent
way to send events.
5 : Event sending method:
private void sendEvent(String eventName, @Nullable WritableMap params) {
this.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
}
Front end implementation
Before starting the front-end part, let me say a few nonsense.
The native modules implemented by Android and iOS must have the same name. The module name must have the same name, and the native method must have the same name. When calling native methods on the front end, you need to ensure that the types are corresponding. For example, the public void sendEventInSeconds(long seconds, Promise promise)
above example is of digital type, so when called by JS, it must be of digital type, otherwise an error will occur. Or, you can provide a custom type conversion method.
On the front end only need:
import {
//...
NativeEventEmitter,
NativeModules,
EmitterSubscription,
} from 'react-native';
const {FillingHoleModule} = NativeModules; // 1
const eventEmitter = new NativeEventEmitter(FillingHoleModule); // 2
const App = () => {
useEffect(() => {
// 3
const eventListener = eventEmitter.addListener('FillingHole', event => {
console.log('You received an event', JSON.stringify(event));
});
listenersRef.current = eventListener;
return () => {
// 4
listenersRef.current?.remove();
};
});
// 5
const handlePress = async () => {
console.log('>', text);
try {
await FillingHoleModule.sendEventInSeconds(+text);
} catch (e) {
console.error('Create event failed, ', e);
}
};
return (
//...
);
}
1 : Get the defined native module NativeModules
2 : Use the defined native module to initialize NativeEventEmiotter
3 : Add a listener for native events
4 : Destroy the listener at the end
5 : When the user clicks the button, the method exposed by the native module is called to setTimeout
method of the native event.
3> and 4> are the content of react hooks, students who are not familiar with it can refer to the official website.
There is also a practical tip here, using useRef
save the listener of component events.
finally
After the preparations are done, you can start running. Shake the phone to turn on the debug mode to view the events received from the native.
There may not be many places used to emit events natively, but it is definitely a very useful feature. Hope this article can help you.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。