Skip to content

NativeModules empty object #26813

@thezealousfool

Description

@thezealousfool

Update

A lot of people facing this problem do not go through all the comments and so I am mentioning my conclusion here. It is not a permanent solution and thus this issue cannot be closed yet, but this is A solution for people who want to continue developing their native modules while the react-native team figure out a solution.

Turns out, turning on debug mode once fixes the problem. Strange..

When we turn on debug mode, it uses Chrome's V8 engine instead which works without a problem. If that's the case, fiddling around with the js-core version will make it work in non-debug mode too.

Original

I tried to recreate the Native Modules for Android guide on React Native's documentation, NativeModules was an empty object. I found a 3 years old Github repo explaining Native Modules on a rather old version of React (https://github.com/promptworks/ReactNativeBridgeExample) which works. I could not find anything wrong in my code. Either I am doing something very silly or there is a bug in the codebase.

React Native version:

react: 16.9.0
react-native: 0.61.2
react-native-cli: 2.0.1

Steps To Reproduce

  1. Create a new react native project using react-cli
  2. Follow the steps mention in https://facebook.github.io/react-native/docs/native-modules-android.html for the Toast module

or

  1. Add Kotlin support to your project and create files as described below (full Kotlin code provided)

Describe what you expected to happen:
NativeModules to have the exported functions instead of being an empty object.

Snack, code example, screenshot, or link to a repository:

Here I provide code for a Kotlin project trying to count the number of clicks. Everything compiles correctly.

Note

I tried without Kotlin as well, exactly copying code from the official React docs but with the same results

Code

gradle.properties:

android.useAndroidX=true
android.enableJetifier=false

app/build.gradle:

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "com.facebook.react:react-native:+"  // From node_modules

    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71"

    if (enableHermes) {
        def hermesPath = "../../node_modules/hermes-engine/android/";
        debugImplementation files(hermesPath + "hermes-debug.aar")
        releaseImplementation files(hermesPath + "hermes-release.aar")
    } else {
        implementation jscFlavor
    }
}

StateModule.kt:

package com.statemanagertest.state

import com.facebook.react.bridge.*

import kotlin.collections.HashMap

class StateModule(context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
    private val constants : HashMap<String, Any> = hashMapOf("clicks" to 0)

    companion object {
        lateinit var reactContext: ReactApplicationContext
    }

    init {
        reactContext = context
    }

    override fun getName(): String {
        return "StateExample"
    }

    override fun getConstants(): HashMap<String, Any> {
        return constants
    }

    @ReactMethod
    fun whenClicked(callback: Callback) {
        val curValAny = constants["clicks"]
        val curVal = if (curValAny is Int) curValAny else 0
        constants["clicks"] = curVal + 1
        callback.invoke()
    }
}

StatePackage.kt:

package com.statemanagertest.state

import android.view.View
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.JavaScriptModule
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ReactShadowNode
import com.facebook.react.uimanager.ViewManager

class StatePackage : ReactPackage {
    override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
        return mutableListOf(StateModule(reactContext))
    }

    override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
        return mutableListOf()
    }

    fun createJSModules(): List<Class<out JavaScriptModule>> {
        return listOf()
    }
}

MainApplication.kt:

package com.statemanagertest

import android.app.Application
import android.content.Context
import com.facebook.react.ReactApplication
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.soloader.SoLoader
import java.lang.reflect.InvocationTargetException
import com.facebook.react.shell.MainReactPackage
import com.statemanagertest.state.StatePackage


class MainApplication : Application(), ReactApplication {

  private val mReactNativeHost = object : ReactNativeHost(this) {
    override fun getUseDeveloperSupport(): Boolean {
      return BuildConfig.DEBUG
    }

    override fun getPackages(): List<ReactPackage> {
      return listOf(
              MainReactPackage(),
              StatePackage()
      )
    }

    override fun getJSMainModuleName(): String {
      return "index"
    }
  }

  override fun getReactNativeHost(): ReactNativeHost {
    return mReactNativeHost
  }

  override fun onCreate() {
    super.onCreate()
    SoLoader.init(this, /* native exopackage */ false)
    initializeFlipper(this) // Remove this line if you don't want Flipper enabled
  }

  private fun initializeFlipper(context: Context) {
    if (BuildConfig.DEBUG) {
      try {
        val aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper")
        aClass.getMethod("initializeFlipper", Context::class.java).invoke(null, context)
      } catch (e: ClassNotFoundException) {
        e.printStackTrace()
      } catch (e: NoSuchMethodException) {
        e.printStackTrace()
      } catch (e: IllegalAccessException) {
        e.printStackTrace()
      } catch (e: InvocationTargetException) {
        e.printStackTrace()
      }

    }
  }
}

App.js:

import React, { Component } from 'react';
import {
  SafeAreaView,
  StyleSheet,
  Text,
  Button,
  NativeModules
} from 'react-native';

class App extends Component {
  constructor() {
    super();
    console.log("vvk::NativeModules:", NativeModules)
  }
  render() {
    return (
      <>
        <SafeAreaView style={styles.container}>
          <Text style={styles.text}>Clicks: {0}</Text>
          <Button title="Click" onPress={() => {console.log(NativeModules)}}/>
        </SafeAreaView>
      </>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: "stretch",
    margin: 20,
  },
  text: {
    textAlign: 'center',
    padding: 20,
    fontSize: 20
  }
});

export default App;

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions