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;
}
Hey @TheWirv, I had the same issue, but on iOS side. The problem was with incorrectly blocked react-native resolution in my example/metro.config.js
.
I had to update react-native version in my library from 0.63.4 to 0.64.2, which resulted in newer version of metro-config
. And that newer version had different path and argument names (exclusionList, blockList instead of blacklist)
My problem was that I incorrectly migrated resolver argument in metro config
What was causing problem
const exclusionList = require('metro-config/src/defaults/exclusionList');
//...
resolver: {
blocklist: exclusionList( // <----- this line
modules.map(
(m) =>
new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
)
),
extraNodeModules: modules.reduce((acc, name) => {
acc[name] = path.join(__dirname, 'node_modules', name);
return acc;
}, {}),
},
Instead of:
blocklist: exclusionList
There should be (capital 'L')
blockList: exclusionList
Make sure that your metro config is correct, because otherwise, view config won't be registered to correct react-native package (the one that will be executed by app)
@TheWirv Were you able to solve the issue?
@mateusz1913 I'm also facing the same problem for iOS, but your proposal also failed for me. I'm trying to setup correct example for react-native-video
, that will use parent directory instead of installed package. Can you provide me with your setup for example & library itself?
@wishfulthinkerme look for react-native-avoid-softinput and check git history for example/metro.config.js
file
Damn it is still the same. It seems that NativeModule is exported correctly, but for Native UI Components I still receive: View config getter callback for component ...
For my case, I have to remove the node_modules directory within the MyNativeModule before import, else it will be loaded and this creates another "module dictionary", beside the existing one belonging to the app
MyNativeModule will be loaded by default in the new module dictionary. But when the app search for the module, it will only search in the module dictionary that belongs to the app, hence that is why it returns undefined.
We have to remove the node_modules within the MyNativeModule to force it to load to the app's module dictionary in order to allow it be found.
if you need to retain the node_modules in the MyNativeModule for further development, then you will need to remove it directly within the imported directory of the app's node_modules folder after the import.
- app
- node_modules
- MyNativeModule
- node_modules <-- remove this
- MyNativeModule
- node_modules
Sorry if the term "module dictionary" is inaccurate and confusing here, let me know the term that I should use.
Hope it helps 😉
node_modules
will be recreated again in the next npm install
? @alyc
Also reproduced when one of mono repo package has alternative react-native version of application. Be careful! And check all package.json for react-native version package.
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
This issue was closed because it has been stalled for 7 days with no activity.