This is documentation for React Native v0.62, which is no longer actively maintained.
For up-to-date documentation, see the latest version (0.63).
Version: 0.62
Native Modules
Sometimes an app needs access to a platform API that React Native doesn't have a corresponding module for yet. Maybe you want to reuse some existing Java code without having to reimplement it in JavaScript, or write some high performance, multi-threaded code such as for image processing, a database, or any number of advanced extensions.
We designed React Native such that it is possible for you to write real native code and have access to the full power of the platform. This is a more advanced feature and we don't expect it to be part of the usual development process, however it is essential that it exists. If React Native doesn't support a native feature that you need, you should be able to build it yourself.
Native modules are usually distributed as npm packages, apart from the typical javascript files and resources they will contain an Android library project. This project is (from NPM's perspective) similar to any other media asset, meaning there isn't anything unique about it from this point of view. To get the basic scaffolding make sure to read Native Modules Setup guide first.
This guide will use the Toast example. Let's say we would like to be able to create a toast message from JavaScript.
We start by creating a native module. A native module is a Java class that usually extends the ReactContextBaseJavaModule class and implements the functionality required by the JavaScript. Our goal here is to be able to write ToastExample.show('Awesome', ToastExample.SHORT); from JavaScript to display a short toast on the screen.
Create a new Java Class named ToastModule.java inside android/app/src/main/java/com/your-app-name/ folder with the content below:
ReactContextBaseJavaModule requires that a method called getName is implemented. The purpose of this method is to return the string name of the NativeModule which represents this class in JavaScript. So here we will call this ToastExample so that we can access it through React.NativeModules.ToastExample in JavaScript.
@Override
publicStringgetName(){
return"ToastExample";
}
An optional method called getConstants returns the constant values exposed to JavaScript. Its implementation is not required but is very useful to key pre-defined values that need to be communicated from JavaScript to Java in sync.
To expose a method to JavaScript a Java method must be annotated using @ReactMethod. The return type of bridge methods is always void. React Native bridge is asynchronous, so the only way to pass a result to JavaScript is by using callbacks or emitting events (see below).
The last step within Java is to register the Module; this happens in the createNativeModules of your apps package. If a module is not registered it will not be available from JavaScript.
create a new Java Class named CustomToastPackage.java inside android/app/src/main/java/com/your-app-name/ folder with the content below:
The package needs to be provided in the getPackages method of the MainApplication.java file. This file exists under the android folder in your react-native application directory. The path to this file is: android/app/src/main/java/com/your-app-name/MainApplication.java.
// MainApplication.java
...
importcom.your-app-name.CustomToastPackage;// <-- Add this line with your package name.
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(newCustomToastPackage());// <-- Add this line with your package name.
return packages;
}
To access your new functionality from JavaScript, it is common to wrap the native module in a JavaScript module. This is not necessary but saves the consumers of your library the need to pull it off of NativeModules each time. This JavaScript file also becomes a good location for you to add any JavaScript side functionality.
Create a new JavaScript file named ToastExample.js with the content below:
/**
* This exposes the native ToastExample module as a JS module. This has a
* function 'show' which takes the following parameters:
*
* 1. String message: A string with the text to toast
* 2. int duration: The duration of the toast. May be ToastExample.SHORT or
* ToastExample.LONG
*/
import{ NativeModules }from'react-native';
module.exports = NativeModules.ToastExample;
Now, from your other JavaScript file you can call the method like this:
import ToastExample from'./ToastExample';
ToastExample.show('Awesome', ToastExample.SHORT);
Please make sure this JavaScript is in the same hierarchy as ToastExample.js.
This method would be accessed in JavaScript using:
UIManager.measureLayout(
100,
100,
(msg)=>{
console.log(msg);
},
(x, y, width, height)=>{
console.log(x +':'+ y +':'+ width +':'+ height);
}
);
A native module is supposed to invoke its callback only once. It can, however, store the callback and invoke it later.
It is very important to highlight that the callback is not invoked immediately after the native function completes - remember that bridge communication is asynchronous, and this too is tied to the run loop.
Native modules can also fulfill a promise, which can simplify your JavaScript, especially when using ES2016's async/await syntax. When the last parameter of a bridged native method is a Promise, its corresponding JS method will return a JS Promise object.
Refactoring the above code to use a promise instead of callbacks looks like this:
The JavaScript counterpart of this method returns a Promise. This means you can use the await keyword within an async function to call it and wait for its result:
Native modules should not have any assumptions about what thread they are being called on, as the current assignment is subject to change in the future. If a blocking call is required, the heavy work should be dispatched to an internally managed worker thread, and any callbacks distributed from there.
Native modules can signal events to JavaScript without being invoked directly. The easiest way to do this is to use the RCTDeviceEventEmitter which can be obtained from the ReactContext as in the code snippet below.
this.eventListener.remove();//Removes the listener
}
Getting activity result from startActivityForResult#
You'll need to listen to onActivityResult if you want to get results from an activity you started with startActivityForResult. To do this, you must extend BaseActivityEventListener or implement ActivityEventListener. The former is preferred as it is more resilient to API changes. Then, you need to register the listener in the module's constructor,
Now you can listen to onActivityResult by implementing the following method:
@Override
publicvoidonActivityResult(
finalActivity activity,
finalint requestCode,
finalint resultCode,
finalIntent intent){
// Your logic here
}
We will implement a basic image picker to demonstrate this. The image picker will expose the method pickImage to JavaScript, which will return the path of the image when called.
Listening to the activity's LifeCycle events such as onResume, onPause etc. is very similar to how we implemented ActivityEventListener. The module must implement LifecycleEventListener. Then, you need to register a listener in the module's constructor,
reactContext.addLifecycleEventListener(this);
Now you can listen to the activity's LifeCycle events by implementing the following methods: