← Back to blog
Send events from Kotlin code to JS Engine using Native Modules in React Native Android appPhoto via Unsplash
React NativeKotlinTypescript

Send events from Kotlin code to JS Engine using Native Modules in React Native Android app

Learn how to send events from Kotlin code to Typescript layer in React Native Android app

Suyash Singh
Suyash Singh
2 min read

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:

 
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;

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 using RCTDeviceEventEmitter.
  • 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.