The paper

In the process of React Native development, the React technology stack can be used to realize UI and front-end interaction functions for front-end development, but many complicated functions are actually implemented at the Native end. The React Native development mode requires both front-end RN development and Android and IOS Native development, which is called hybrid development.

Mixed development scenario

Hybrid development means that the development team has to have both Android and IOS skills as well as front-end development capabilities, which is a luxury for the development team. At present, React Native libraries are abundant. Most of the functions can be found on Github, shielding the Native technology stack for developers. However, it is inevitable that some functions need to call some APIS of Native end: for example, the processing of Gzip compressed files in Weizi App, the call of existing SDK such as playing videos and entering the live broadcast room.

The Native end interacts with RN code

The interaction between the Native terminal and RN should contain at least two parts:

  1. RN actively invokes Native modules and returns the results of module execution through promises
  2. For asynchronous task-type methods, you can listen for events in Native callbacks by listening for events in RN

Android interacts with RN code

React in the Android Native provide us ReactContextBaseJavaModule base class, we only need to integrate the base class, rewrite the getName method, the return value is the name of our custom module, Finally, adding the package to the MainApplication entry completes the addition of a custom module.

// DemoPackage.java public class DemoPackage implements ReactPackage { @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new DemoModule(reactContext)); return modules; } } // DemoModule.java public class DemoModule extends ReactContextBaseJavaModule { ReactApplicationContext reactContext; public DemoModule(ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; } @ReactMethod public void fun(String data, Promise promise) { promise.resolve("{\"data\": \""+data+"\"}"); } @ReactMethod public void fun1(String data, Promise promise) { sendEvent(reactContext, "ON_NATIVE", "{\"data\": \""+data+"\"}"); } public static void sendEvent(ReactContext reactContext, String eventName, @Nullable String params) { reactContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit(eventName, params); } @Override public String getName() { return "DemoModule"; }}Copy the code

IOS interacts with RN code

// DemoModule.h #import <Foundation/Foundation.h> #import <React/RCTBridgeModule.h> #import <React/RCTEventEmitter.h> @interface DemoModule : RCTEventEmitter <RCTBridgeModule> @end // DemoModule.m #import <Foundation/Foundation.h> #import "AppDelegate.h" #import  "DemoModule.h" #import <React/RCTBridgeModule.h> #import <UIKit/UIKit.h> @implementation DemoModule RCT_EXPORT_MODULE(); - (NSArray<NSString *> *)supportedEvents { return @[@"ON_NATIVE"]; } RCT_REMAP_METHOD(fun, fun: (NSString *)data resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSString* result = [NSString stringWithFormat:@"{\"data\": \"%@\"}", data]; resolve(result); } RCT_EXPORT_METHOD(fun1:(NSString *)data) { NSString* result = [NSString stringWithFormat:@"{\"data\": \"%@\"}", data]; [self.bridge enqueueJSCall:@"RCTDeviceEventEmitter" method:@"emit" args:@[@"ON_NATIVE", result] completion:NULL]; } @endCopy the code

RN interacts with Android and IOS

import { NativeModules, DeviceEventEmitter } from 'react-native';
const { DemoModule } = NativeModules;
DeviceEventEmitter.addListener('ON_NATIVE', (result) => {
  // do something
});
DemoModule.fun('test').then(result => {
  // do something
}).catch(error => {
  // error
});
DemoModule.fun1('test');
Copy the code

RN modular hybrid development

The code interaction between RN and Native can be realized in the above way, but there is a drawback that Native code is mixed with the project and cannot be stripped and shared with other projects. Every time a Native function module is added, it needs to be modified in the basic framework code, which is very inconvenient.

If we think carefully, we will find that the relevant Native dependencies introduced through the NPM package do not need to modify the framework code every time, so we can also peel off the Native related functions and encapsulate them into one NPM module. Through this modular hybrid development, the above problems can be solved.

Create React Native Module

React Native supports AutoLink since version 0.60. You don’t need to perform React Native Link after each yarn install. RN Native has the following code that automatically introduces relevant dependencies into Native:

// IOS Podfile require_relative '.. /node_modules/react-native/scripts/react_native_pods' require_relative '.. /node_modules/@react-native-community/cli-platform-ios/native_modules // Android setting.gradle apply from: file(".. /node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)Copy the code

Therefore, we only need to organize our modules according to the specific structure and introduce them in this AutoLink way. The Create React Native Module helps us to automatically generate a React Native module template.

  1. Install dependencies
npm install -g create-react-native-module
Copy the code
  1. Create a project

Create a module project template with example

create-react-native-module MyFancyLibrary --generate-example
Copy the code
  1. Structure that
├── LICENSE       // 开源证书
├── README.md     // 说明文档
├── android       // Android工程代码
├── example       // 完整RN项目,已引入当前库
├── index.js      // RN暴露模块
├── ios           // IOS工程代码
├── package.json 
└── react-native-gzip.podspec
Copy the code
  1. The module adds third-party dependencies

How to add third-party dependencies to the Create React Native Module and AutoLink it?

  • Android
// ./android/build.gradle dependencies { //noinspection GradleDynamicVersion implementation 'com.facebook.react:react-native:+' // From node_modules implementation 'commons-io:commons-io:2.6' implementation 'org.apache.com mons: the Commons - compress: 1.1}Copy the code
  • IOS
// ./MODULE_NAME.podspec
s.dependency "NVHTarGzip"
Copy the code

Publish to the NPM library

To facilitate use, we need to publish the encapsulated RN module to NPM. Before publishing, we need to modify your package.json and Readme. For specific instructions, please refer to relevant materials on the Internet. When you are ready, just execute in the root directory:

NPM login // Enter the account password email information NPM publishCopy the code

After a successful release, the library can be introduced normally when needed

To extract gzip files, Create a NPM package using the Create React Native Module.

conclusion

Developing an APP using React Native alone always faces various restrictions. Hybrid development opens the door to a new world for us. As long as there is a corresponding solution in The Native end, we can modify it and use it in React Native. But it also requires an understanding of both objective-C for IOS and Java for Android, which is a long way to go.