-
Notifications
You must be signed in to change notification settings - Fork 25.1k
Description
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
- Create a new react native project using react-cli
- Follow the steps mention in https://facebook.github.io/react-native/docs/native-modules-android.html for the Toast module
or
- 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;