In this post, we will demonstrate how to send events from Kotlin to the JavaScript engine using native modules in a React Native app. We will use a simple example where the backend calculates temperature using a sensor and the React Native app displays it.
Essentially, we need to emit messages to the DeviceEventManagerModule.RCTDeviceEventEmitter
class which passes the messages to the JS Instance using the React Native Bridge
.
Step-by-Step Guide
1. Setting Up the Kotlin Module
Create a Kotlin file BackendModule.kt with the following code:
In this post we are going to create a BackendModule.kt
file, which should be registered as a native module in our react native app. You can learn to create and register custom native modules packages here.
package com.sampleapp
import kotlin.random.Random
import com.facebook.react.bridge.Promise
import android.util.Log
import com.sampleapp.uniffi.rustyHello
import com.sampleapp.uniffi.rustyInit
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.WritableMap
import com.facebook.react.modules.core.DeviceEventManagerModule
class BackendModule internal constructor(context: ReactApplicationContext?) : ReactContextBaseJavaModule(context) {
override fun getName(): String {
return "BackendModule"
}
private var listenerCount = 0
val TEMPERATURE_SENSOR = "TEMPERATURE_SENSOR"
@ReactMethod
fun recordTemperatures() {
updateTemperature()
}
@ReactMethod
fun addListener(_eventName: String) {
listenerCount += 1
}
@ReactMethod
fun removeListeners(count: Int) {
listenerCount -= count
}
fun updateTemperature() {
Thread {
while (true) {
readTemperature()
Thread.sleep(1000)
}
}
.start()
}
private fun sendEvent(reactContext: ReactContext, eventName: String, params: WritableMap?) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit(eventName, params)
}
// Function to generate a random temperature between 5 and 50
private fun getTemperature(): Int {
return Random.nextInt(5, 51) // Generates a random integer between 5 (inclusive) and 51 (exclusive)
}
// Function to read temperature data and send it as an event
private fun readTemperature() {
val temperature = getTemperature()
val params = Arguments.createMap().apply {
putInt("temperature", temperature)
}
sendEvent(getReactApplicationContext(), TEMPERATURE_SENSOR, params)
}
}
2. Setting Up the React Native Component
Now we need to create a React Native component which can subscribe to these events emitted by our BackendModule
Native Module
.
import React, {useEffect, useState} from 'react';
import {
NativeEventEmitter,
NativeModules,
SafeAreaView,
Text,
View,
} from 'react-native';
const {BackendModule} = NativeModules;
const Temperature = () => {
const [temperature, setTemperature] = useState(0);
const eventEmitter = new NativeEventEmitter(NativeModules.BackendModule);
useEffect(() => {
const eventListener = eventEmitter.addListener(
'TEMPERATURE_SENSOR',
event => {
setTemperature(event.temperature);
},
);
BackendModule.recordTemperatures();
return () => {
eventListener.remove();
};
}, []);
return (
<View className="h-screen w-screen items-center justify-center">
<Text className="text-6xl">{`${temperature} ° C`}</Text>
</View>
);
};
function App(): JSX.Element {
return (
<SafeAreaView>
<Temperature />
</SafeAreaView>
);
}
export default App;
In this post we are using TailwindCSS with react native. Learn how to set it up here in a previous post I have written on it.
Explanation
BackendModule.kt
:
getTemperature()
: Generates a random temperature between 5 and 50.readTemperature()
: Reads the temperature and sends it as an event to the JavaScript engine.sendEvent()
: Sends the temperature event to the JavaScript engine usingRCTDeviceEventEmitter
.recordTemperatures()
: Starts a thread that continuously reads the temperature every second and sends it as an event.
App.tsx
:
Temperature Component:
- Sets up a
useEffect
hook to listen for temperature events from the backend. - Calls
BackendModule.recordTemperatures()
to start recording temperatures. - Updates the temperature state when a new event is received and displays it.
Conclusion
This example demonstrates how to use native modules in a React Native app to send events from Kotlin to the JavaScript instance. By following these steps, you can set up your backend to calculate data (e.g., temperature from a sensor) and display it in your React Native app. This approach can be extended to handle more complex interactions between your native code and React Native app.