RN development usually involves communication between JS and Native, such as: When initializing RN, Native transmits data to JS, JS calls Native’s album to select pictures, JS calls Native’s module to perform some complex calculations, and Native actively transmits some data (GPS information, gyroscope, sensor, etc.) to JS, etc.

In this article, I will introduce several ways of communication between JS and Native in RN, as well as their principles and usage skills.

Next, I will introduce the communication between JS and Native in different scenarios.

Several communication scenarios:

  • When initializing RN, Native passes data to JS;
  • Native sends data to JS;
  • JS sends data to Native;
  • JS sends data to Native, and Native sends data back to JS;

1. When initializing RN, Native transmits data to JS

RN’s API provides a way for Native to transmit data to JS when initializing the JS page, which occurs earlier than the other ways of transmitting data described below.

Because there is very little information about this way, so many friends may not know this way, but no matter, next I will show you how to use this way to pass data to JS.

concept

RN allows us to pass props data to the top-level JS component when we initialize the JS page. The top-level component can get this data through this.props.

iOS

[[RCTRootView alloc] initWithBundleURL: jsCodeLocation moduleName: self. ModuleName // this"App1"The name must be the same as the name we registered in index.js initialProperties:@{@"params":self.paramsInit}//RN initializes the data passed to JS launchOptions: nil];Copy the code

Next, let’s take a look at how to pass this initialization data on iOS.

IOS passes initialization data to RNinitialProperties

RN’s RCTRootView provides the initWithBundleURL method to render a JS component, in which a parameter is provided to pass initialization data to the JS component.

Method prototype:

- (instancetype)initWithBundleURL:(NSURL *)bundleURL moduleName:(NSString *)moduleName
		initialProperties:(NSDictionary *)initialProperties launchOptions:(NSDictionary *)launchOptions
Copy the code
  • jsCodeLocation: path to RN’s JS page to render;
  • moduleName: Name of the JS module to load;
  • initialProperties: To pass toTop-level JS componentsInitialization data of;
  • launchOptionsThis is mainly used when AppDelegate loads JS bundles. In this case, pass nil.

An NSDictionary-type data is passed to the top-level JS component using the third parameter of the above method.

Sample code:

[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
                                moduleName: self.moduleName
                         initialProperties:@{@"params": @"This is the data passed to the top-level JS component."}// initializing data that RN passes to JS launchOptions: nil];Copy the code

In the above code, we pass a data named params which is passed to the top-level JS component to the top-level JS component. Then the top-level JS component can retrieve this data by using the following method:

 render() {
        const {params}=this.props;
        return(<View style={styles.container}> <Text style={styles.data}> from Native initialization data: {params}</Text> </View>); }Copy the code

Alternatively, if you want to use this initialization data in a non-top-level page such as CommonPage, you can pass the data to the CommonPage page as follows:

export default class App extends Component<Props> {
	...
    render() {
        return <CommonPage  {...this.props}/>;
    }
    ...
}
Copy the code

2. Communication from Native to JS (sending data to JS from Native)

RN’s iOS SDK provides an interface RCTEventEmitter, through which we can realize the communication from Native to JS, that is, data transfer from Native to JS.

Method prototype:

- (void)sendEventWithName:(NSString *)name body:(id)body;
Copy the code

So as long as we get an instance of RCTEventEmitter we can use it to pass data to JS. To get examples of RCTEventEmitter

, we can inherit RCTEventEmitter

:

DataToJSPresenter.h

/ * * * * Author React Native JS Native communication: CrazyCodeBoy * video tutorial: https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:[email protected]
 */
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface DataToJSPresenter : RCTEventEmitter <RCTBridgeModule>

@end
Copy the code

DataToJSPresenter.m

/ * * * * Author React Native JS Native communication: CrazyCodeBoy * video tutorial: https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:[email protected]
 */
#import "DataToJSPresenter.h"

@implementation DataToJSPresenter

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
    return@ [@"testData"];
}
- (instancetype)init {
    if(self = [super init]) {// Register fireData broadcast [[NSNotificationCenter defaultCenter] addObserver:self at module initialization selector:@selector(fireData:) name:@"fireData" object:nil];
    }
    returnself; } - (void)fireData:(NSNotification *)notification{// send data to RN NSString *eventName = notification.object[@"name"];
    NSDictionary *params = notification.object[@"params"];
    [self sendEventWithName:eventName body:params];
}

@end
Copy the code

In the above method, we pass the data params named eventName to JS via RCTEventEmitter’s sendEventWithName method.

Tip: In DataToJSPresenter we implement the (NSArray

*)supportedEvents method, which specifies the name of the event that can be sent to JS, So the eventName sent to JS must be configured in this method otherwise it cannot be sent.

Steps required to implement Native to JS communication

Next, we summarize the steps required to implement Native to JS communication:

  • The first thing is to implementRCTEventEmitter <RCTBridgeModule>;
  • throughRCTEventEmitterthesendEventWithNameMethod to pass data to JS;

Through the above steps, we can launch the data from Native to JS, so how to obtain the data in JS?

Get Native pass in JSRCTEventEmitterIncoming data

In JS, NativeEventEmitter can be used to obtain the data sent by Native RCTEventEmitter. The specific methods are as follows:

import {NativeEventEmitter} from 'react-native';
export default class CommonPage extends Component<Props> {
    constructor(props) {
        super(props);
        this.state = {
            data: "",
            result: null
        }
    }

    componentWillMount() {
        this.dataToJSPresenter = new NativeEventEmitter(NativeModules.DataToJSPresenter);
        this.dataToJSPresenter.addListener('testData', (e) => {// for iOS
            this.setState({
                data: e.data
            })
        })
	}

    componentWillUnmount() {
        if (this.dataToJSPresenter){
            this.dataToJSPresenter.removeListener('testData'); }}render() {
        return(<View style={styles.container}> <Text style={styles.data}> Receive Native data: {this.state.data}</Text> </View>); }}Copy the code

In the code above, we add a listener to NativeEventEmitter’s addListener. This listener will listen to data sent from Native named testData, which should be consistent with the eventName mentioned above:

[self sendEventWithName:eventName body:params];
Copy the code

Coding.imooc.com/lesson/89.h… Also, remember to remove listeners when the JS component is uninstalled.

The above is the principle and way to realize the communication between Native and JS in iOS. Next, let’s take a look at the principle and way to realize the communication between JS and Native.

3. JS to Native communication (JS sends data to Native)

The NativeModule package is for JS. It is a bridge between JS and Native. JS can communicate with Native people through NativeModule (transferring data, opening Native pages, etc.).

How do you implement NativeModule with React Native

First, implement JSBridgeModule

First we need to implement RCTBridgeModule:

JSBridgeModule.h

/ * * * * Author React Native JS Native communication: CrazyCodeBoy * video tutorial: https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:[email protected]
 */
#import <React/RCTBridgeModule.h>
@interface JSBridgeModule : NSObject <RCTBridgeModule>

@end
Copy the code

JSBridgeModule.m

/ * * * * Author React Native JS Native communication: CrazyCodeBoy * video tutorial: https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:[email protected]
 */
#import "JSBridgeModule.h"

@implementation JSBridgeModule

RCT_EXPORT_MODULE();
- (dispatch_queue_t)methodQueue
{
    returndispatch_get_main_queue(); RCT_EXPORT_METHOD(sendMessage: NSDictionary*)params){// Accept a message from RN [[NSNotificationCenter defaultCenter] postNotificationName:@"sendMessage" object:params];
}
@end
Copy the code

Code parsing

  1. inJSBridgeModuleIn, we implemented oneRCT_EXPORT_METHOD(sendMessage:(NSDictionary*)params)Method, which is mainly used to expose JS calls to pass dataparamsTo the Native;
  2. After receiving the data, passNSNotificationCenterSending data in the form of notifications;

JS calls JSBridgeModule to send data to Native

import {NativeModules} from 'react-native';

const JSBridge = NativeModules.JSBridgeModule;

JSBridge.sendMessage({text: this.text})
Copy the code

With the above code I can pass a Map type of data {text: this.text} to Native.

4. JS sends data to Native, and Native sends data back to JS

Through the JS to Native communication mentioned above (JS sends data to Native), we have realized THE JS to Native communication. At that time, we relied on JSBridgeModule. In fact, its functions are not limited to this, with which we can also realize the data transmission from Native to JS.

Implementation in Native

Add the following methods to the JSBridgeModule:

RCT_EXPORT_METHOD(doAdd:(NSInteger )num1 num2:(NSInteger )num2 resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    NSInteger result=num1+num2;
    resolve([NSString stringWithFormat:@"%ld",(long)result]); // callback JS}Copy the code

This code exposes JS to a simple addition between two integers and passes the result back to JS using the RCTPromiseResolveBlock and RCTPromiseRejectBlock callbacks representing success and failure, respectively.

Implementation in JS

import {NativeModules} from 'react-native';

const JSBridge = NativeModules.JSBridgeModule;
JSBridge.doAdd(parseInt(this.num1), parseInt(this.num2)).then(e => {
    this.setState({
        result: e
    })
})
Copy the code

In JS, we pass two integers NUM1 and num2 to Native through JSBridge. DoAdd method, and then monitor the return result, the whole process adopts the chain call method of Promise.

reference

  • Example source code download
  • React Native is cross-platform
  • native-modules-android