react-native
react-native copied to clipboard
Android Native UI Module: "View config getter callback for component `MyNativeModule` must be a function (received `undefined`)."
Description
Hey all, so in lack of any real onFocus
/onBlur
functionality (except for TextInput
obv), I've been trying to build my own component as a native module. I've been following both the docs for plain native modules and UI modules, I used create-react-native-library
to scaffold the project (in "Obj-C+Java" config), and you can see my current progress so far below.
The problem is the following: When I'm trying to run the app (after building the Android app and the JS bundle), I get the following error message: Invariant Violation: View config getter callback for component `OnChangeFocusView` must be a function (received `undefined`).
I dug around a little and found out that ReactNativeViewConfigRegistry.register
is throwing this invariant violation. To test out what's going on, I threw a couple of console.log
s in there, and it seems like something is really f***ed up because the error complains that register
has received undefined
for its callback
argument, while my logs show that it was not undefined
, and indeed of type function
(see images below).
I also looked to the React Native Gesture Handler library for inspiration (especially their RNGestureHandlerRootViewManager.java
and RNGestureHandlerRootView.java
classes), but I can't see anything they do that I don't.
Man..and I haven't even gotten to iOS, yet.
createReactNativeComponentClass.js
ReactNativeViewConfigRegistry.js
debugger-ui
React Native version:
Run react-native info
in your terminal and copy the results here.
RN Info
System:
OS: Windows 10 10.0.19042
CPU: (8) x64 Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz
Memory: 475.23 MB / 15.85 GB
Binaries:
Node: 15.12.0 - ~\AppData\Local\Temp\yarn--1620743931304-0.6946422970609345\node.CMD
Yarn: 1.22.5 - ~\AppData\Local\Temp\yarn--1620743931304-0.6946422970609345\yarn.CMD
npm: 7.6.3 - C:\Program Files\nodejs\npm.CMD
Watchman: Not Found
SDKs:
Android SDK:
API Levels: 23, 25, 26, 27, 28, 29, 30
Build Tools: 28.0.3, 29.0.0, 29.0.1, 29.0.2, 29.0.3, 30.0.0, 30.0.1, 30.0.2, 30.0.3, 31.0.0, 31.0.0
System Images: android-18 | Google APIs Intel x86 Atom, android-19 | Google APIs Intel x86 Atom, android-27 | Google Play Intel x86 Atom, android-28 | Intel x86 Atom, android-28 | Intel x86 Atom_64, android-28 | Google APIs Intel x86 Atom, android-28 | Google APIs Intel x86 Atom_64, android-28 | Google Play Intel x86 Atom, android-28 | Google Play Intel x86 Atom_64, android-29 | Intel x86 Atom_64, android-29 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom_64
Android NDK: Not Found
Windows SDK:
AllowAllTrustedApps: Disabled
Versions: 10.0.14393.0, 10.0.19041.0
IDEs:
Android Studio: Version 4.2.0.0 AI-202.7660.26.42.7322048
Visual Studio: 16.9.31205.134 (Visual Studio Enterprise 2019)
Languages:
Java: 13.0.2 - C:\Program Files\Java\jdk-13.0.2\bin\javac.EXE
npmPackages:
@react-native-community/cli: Not Found
react: 17.0.2 => 17.0.2
react-native: 0.64.1 => 0.64.1
react-native-windows: Not Found
npmGlobalPackages:
*react-native*: Not Found
Steps To Reproduce
Provide a detailed list of steps that reproduce the issue.
- Create a new native module via
create-react-native-library
- Set it up like I did
- Run the app
Expected Results
The app is supposed to run and show my component. :(
Snack, code example, screenshot, or link to a repository:
Here's my code so far Edit: I have published a version of this code to GitHub: https://github.com/TheWirv/rn-native-module-test
Code
NativeModulesPackage.java
package com.my.package;
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.Arrays;
import java.util.Collections;
import java.util.List;
public class NativeModulesPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(new OnChangeFocusViewManager());
}
}
OnChangeFocusViewManager.java
package com.my.package;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import java.util.Map;
@ReactModule(name = OnChangeFocusViewManager.REACT_CLASS)
public class OnChangeFocusViewManager extends ViewGroupManager<OnChangeFocusView> {
public static final String REACT_CLASS = "OnChangeFocusView";
@Override
@NonNull
public String getName() {
return REACT_CLASS;
}
@Override
@NonNull
public OnChangeFocusView createViewInstance(@NonNull ThemedReactContext context) {
return new OnChangeFocusView(context);
}
@Override
public @Nullable
Map<String, Object> getExportedCustomDirectEventTypeConstants() {
return MapBuilder.of(
"onFocus",
MapBuilder.of("phasedRegistrationNames", "onFocus"),
"onBlur",
MapBuilder.of("phasedRegistrationNames", "onBlur"));
}
}
OnChangeFocusView.java
package com.my.package;
import android.content.Context;
import android.view.View;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.facebook.react.views.view.ReactViewGroup;
public class OnChangeFocusView extends ReactViewGroup implements View.OnFocusChangeListener {
public OnChangeFocusView(Context context) {
super(context);
}
private void dispatchOnBlur() {
WritableMap event = Arguments.createMap();
ReactContext reactContext = (ReactContext) getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "onBlur", event);
}
private void dispatchOnFocus() {
WritableMap event = Arguments.createMap();
ReactContext reactContext = (ReactContext) getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "onFocus", event);
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
dispatchOnFocus();
} else {
dispatchOnBlur();
}
}
}
index.tsx
import {requireNativeComponent} from 'react-native';
// Types
import type {ViewStyle} from 'react-native';
import type {PropsWithChildren} from 'react';
type OnChangeFocusViewProps = PropsWithChildren<{
onBlur?: () => void;
onFocus?: () => void;
style?: ViewStyle;
}>;
export const OnChangeFocusView =
requireNativeComponent<OnChangeFocusViewProps>('OnChangeFocusView');
example/src/App.tsx
import * as React from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {OnChangeFocusView} from 'my-native-modules';
export default function App() {
return (
<View style={styles.container}>
<OnChangeFocusView
onBlur={() => console.log('Blurred')}
onFocus={() => console.log('Focused')}
style={styles.box}>
<Text>Touch Me!</Text>
</OnChangeFocusView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'white',
},
box: {
width: 60,
height: 60,
marginVertical: 20,
backgroundColor: 'red',
},
});
inside example/android/app/src/main/java/com/example/nativemodules/MainApplication.java
import com.my.package.NativeModulesPackage;
[...]
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for NativeModulesExample:
// packages.add(new MyReactNativePackage());
packages.add(new NativeModulesPackage());
return packages;
}