React Native allows you to write native code in Java and access it from JavaScript. This can be particularly useful when you need to access native functionality that isn’t available out of the box in React Native.
Key Concepts
- Bridging: Allows JavaScript and native code to communicate.
- Native Modules: Java classes that are exposed to JavaScript.
- Native UI Components: Native views that can be used in React Native’s JavaScript code.
- Events: Mechanism to send data from native code to JavaScript.
Practical Example
Let’s create a simple example where we:
- Create a native module in Java.
- Call this module from JavaScript.
- Pass data from JavaScript to native code and vice versa.
- Handle events from native to JavaScript.
- Create a native UI component.
ANDROID PART:
Step 1: Create the Native Module Class
First, create a native module in Java(ie Java class).
Create a new Java class in the android/app/src/main/java/com/yourappname directory:
package com.rn_nativemodules;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
// This class serves as a bridge between the native Android code and the React Native JavaScript code
// It extends ReactContextBaseJavaModule, which is a base class for native modules in React Native
public class MyNativeModule extends ReactContextBaseJavaModule {
// Define the module name that will be used to reference this module in React Native
private static final String MODULE_NAME ="MyNativeModule";
private ReactApplicationContext reactContext;
// Constructor
public MyNativeModule(@Nullable ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}
// Return the name of the module
@NonNull
@Override
public String getName() {
return MODULE_NAME;
}
// Define native methods that can be called from React Native using @ReactMethod annotation
// Method to log a message received from React Native
@ReactMethod
public void myMethod(String param){
System.out.println("GET DATA FROM RN <--- "+param);
}
// Method to send data back to React Native with a promise
@ReactMethod
public void myMethodWithPromise(String param, Promise promise){
// Check if the parameter is not null
if (param != null) {
// Resolve the promise with a success message
promise.resolve("SEND DATA TO RN ---> " + param);
} else {
// Reject the promise with an error code and message
promise.reject("ERROR_CODE", "Error message explaining failure");
}
}
// You can define more native methods here to be called from React Native
// For example, adding and removing event listeners
// Method to send an event to React Native
private void sendEvent(ReactContext reactContext, String eventName, WritableMap params) {
// Access the device event emitter module and emit an event with parameters
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
// Example method triggered from React Native to send an event
@ReactMethod
public void triggerEvent() {
// Create parameters to send with the event
WritableMap params = new WritableNativeMap();
params.putString("message", "Hello from native code!");
// Call the sendEvent method to emit "MyEvent" with params to React Native
sendEvent(getReactApplicationContext(), "MyEvent", params);
}
}
Summary:
1. Creating the Native Module:
• Class Definition: Extend ReactContextBaseJavaModule to create a native module.
• Module Name: Define a unique module name using the getName() method.
• Constructor: Initialize the module with ReactApplicationContext.
2. Defining Native Methods:
• Simple Methods: Use @ReactMethod annotation to define methods that can be called from JavaScript.
• Logging: Create methods to log messages from JavaScript.
• Promise Handling: Implement methods that use promises for asynchronous operations, resolving or rejecting based on logic.
• Event Emission: Create methods to send events from native code to JavaScript using DeviceEventManagerModule.RCTDeviceEventEmitter.
3. Example Methods:
• myMethod(String param): Logs a message.
• myMethodWithPromise(String param, Promise promise): Resolves or rejects a promise based on the input.
• triggerEvent(): Emits an event to JavaScript with predefined parameters.
Step 2: Register the Module
Next, you need to register the module in a Package class:
package com.rn_nativemodules;
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// This class implements the ReactPackage interface, which is responsible for providing NativeModules and ViewManagers to React Native
// It acts as a package manager for bridging native code with React Native
public class MyNativeModulePackage implements ReactPackage {
// Method to create and return NativeModules to be used by React Native
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactApplicationContext) {
List<NativeModule> modules = new ArrayList<>();
// Add your custom NativeModule implementations to the list
modules.add(new MyNativeModule(reactApplicationContext)); // Add MyNativeModule as a NativeModule
return modules;
}
Step 3: Add the Package to the Main Application
Add your package to the list of packages in your MainApplication.java:
// android/app/src/main/java/com/yourappname/MainApplication.java
import com.yourappname.MyNativeModulePackage; // Add this import
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new MyNativeModulePackage()); // Add this line
return packages;
}
JAVASCRIPT PART:
Step 1: Import and Use NativeModules
import {
Button,
DeviceEventEmitter,
NativeModules,
requireNativeComponent,
StyleSheet,
View,
} from 'react-native';
import React, {useEffect} from 'react';
const {MyNativeModule} = NativeModules;
// Android Files Modified for NativeModules:
// 1. MyNativeModule.java
// 2. MyNativeModulePackage.java
// 3. MainApplication.java (getPackages())
const AndroidNativeModules = () => {
// Define a function to send data to the native Android module
const sendDataToAndroid = () => {
// Call the native method without using a promise
MyNativeModule.myMethod('THIS IS SENT FROM RN TO ANDROID');
};
// Define a function to get data from the native Android module
const getDataFromAndroid = () => {
// Call the native method with a promise
MyNativeModule.myMethodWithPromise('Sent From RN To A then from A to RN')
// Handle the resolved promise (success case)
.then(result => {
// Log the success result
console.log('Success:', result);
})
// Handle the rejected promise (error case)
.catch(error => {
// Log the error information
console.error('Error:', error.code, error.message);
});
};
// To handle events, we need to use RCTDeviceEventEmitter on the native side and DeviceEventEmitter on the JavaScript side.
useEffect(() => {
const subscription = DeviceEventEmitter.addListener('MyEvent', params => {
console.log('Received event with message:', params.message);
});
// Clean up subscription
return () => {
subscription.remove();
};
}, []);
const triggerNativeEvent = () => {
MyNativeModule.triggerEvent();
};
return (
<View style={styles.main}>
<Button
title="Send Data From RN To Android"
onPress={sendDataToAndroid} // check log in android logcat
/>
<Button
title="Send Data To Android From RN"
onPress={getDataFromAndroid} // check log in react native logcat
/>
<Button
title="Receive event From RN From Android"
onPress={triggerNativeEvent}
/>
</View>
);
};
export default AndroidNativeModules;
const styles = StyleSheet.create({
main: {justifyContent: 'space-evenly', alignItems: 'center', flex: 1},
});
Creating a Native UI Component
Creating a native UI component involves creating a ViewManager.
Step 1: Create the ViewManager
package com.rn_nativemodules;
import android.widget.TextView;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
public class MyCustomViewManager extends SimpleViewManager<TextView> {
public static final String REACT_CLASS = "MyCustomView";
@Override
public String getName() {
return REACT_CLASS;
}
@Override
protected TextView createViewInstance(ThemedReactContext reactContext) {
return new TextView(reactContext);
}
@ReactProp(name = "text")
public void setText(TextView view, String text) {
view.setText(text);
}
}
Step 2: Register the ViewManager
// android/app/src/main/java/com/yourappname/MyPackage.java
// Existing code...
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> viewManagers = new ArrayList<>();
viewManagers.add(new MyCustomViewManager());
return viewManagers;
}
Step 3: Use the Custom View in JavaScript
import { requireNativeComponent } from 'react-native';
const MyCustomView = requireNativeComponent('MyCustomView');
const App = () => {
return (
<MyCustomView style={{ width: 200, height: 50 }} text="Hello from custom view!" />
);
};
export default App;
Top comments (0)