React Native iOS应用白屏:NativeEventEmitter参数不能为空错误排查
解决React Native iOS模拟器白屏及NativeEventEmitter参数错误问题
问题背景
React Native应用构建成功,但安装到iOS模拟器后显示白屏。环境配置:NVM管理Node(版本16.14.2)、Xcode 14.2、2017款MacBook Pro,项目集成了@react-native-firebase的Firestore与推送通知功能。
报错栈
BUNDLE ./index.js ERROR Invariant Violation: `new NativeEventEmitter()` requires a non-null argument. ERROR Invariant Violation: Module AppRegistry is not a registered callable module (calling runApplication). A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native. ERROR Invariant Violation: Module AppRegistry is not a registered callable module (calling runApplication). A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.
相关配置文件
package.json
{ "name": "digitvl_app", "version": "0.0.1", "private": true, "rnpm":{ "assets":["./assets/fonts"] }, "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", "test": "jest", "lint": "eslint ." }, "dependencies": { "@react-native-firebase/app": "^16.5.0", "@react-native-firebase/auth": "^16.5.0", "@react-native-firebase/firestore": "^16.5.0", "@react-native-firebase/messaging": "^16.5.0", "@react-navigation/native": "^6.1.1", "@react-navigation/native-stack": "^6.9.7", "firebase": "^9.15.0", "react": "17.0.2", "react-native": "0.68.2", "react-native-paper": "^5.1.3", "react-native-push-notification": "^8.1.1", "react-native-safe-area-context": "^4.4.1", "react-native-screens": "^3.18.2" }, "devDependencies": { "@babel/core": "^7.20.12", "@babel/runtime": "^7.20.7", "@react-native-community/eslint-config": "^3.2.0", "babel-jest": "^29.3.1", "eslint": "^8.31.0", "jest": "^29.3.1", "metro-react-native-babel-preset": "^0.73.7", "react-test-renderer": "17.0.2" }, "jest": { "preset": "react-native" } }
metro.config.js
module.exports = { transformer: { getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, inlineRequires: true, }, }), }, }; const { getDefaultConfig } = require("metro-config"); const { resolver: defaultResolver } = getDefaultConfig.getDefaultValues(); exports.resolver = { ...defaultResolver, sourceExts: [ ...defaultResolver.sourceExts, "cjs", ], };
App.js
import React, {useEffect, useState} from 'react'; import {StyleSheet, Text, View, Linking, Platform} from 'react-native'; import {NavigationContainer} from '@react-navigation/native'; import {createNativeStackNavigator} from '@react-navigation/native-stack'; import HomeScreen from './screens/HomeScreen'; import LoginScreen from './screens/LoginScreen'; import messaging from '@react-native-firebase/messaging'; import SendNotificationScreen from './screens/SendNotificationScreen'; import PushNotification, {Importance} from 'react-native-push-notification'; import firestore from '@react-native-firebase/firestore'; import auth from '@react-native-firebase/auth' const Stack = createNativeStackNavigator(); export default function App() { const [state,setState] = useState({}) const requestPermission = async () => { const oldToken = await messaging().getToken(); console.log('oldToken:' + oldToken); if(auth().currentUser){ firestore().collection("userToken").doc(auth().currentUser.uid).update({ token:oldToken }) } messaging().onTokenRefresh(token => { console.log('tokenRefresh:' + token); firestore().collection("userToken").add({ token:token }) }); PushNotification.getChannels(function (channel_ids) { console.log('chanells: ' + channel_ids); }); const granted = await messaging() .requestPermission() .catch(err => console.log(err)); if (granted) { console.log('User granted messaging permissions!'); createNotificationListners(); } else { console.log('User declined messaging permissions :('); } await messaging().registerDeviceForRemoteMessages(); return granted; }; useEffect(() => { requestPermission(); PushNotification.configure({ onNotification: notification => { console.log('notificationOpen' + JSON.stringify(notification.data)); setNotificationData(notification.data); }, }); return () => { setState({}); }; }, []); const [noticationData, setNotificationData] = useState({}); useEffect(() => { console.log('notificationReceived: ' + noticationData); Linking.openURL(""+noticationData.url+"").catch(err => console.error("Couldn't load page", err)); }, [noticationData]); const createNotificationListners = async () => { PushNotification.createChannel( { channelId: 'channel-id', channelName: 'My channel', channelDescription: 'A channel to categorise your notifications', playSound: true, soundName: 'notification.mp3', importance: Importance.HIGH, vibrate: true, }, created => console.log(`createChannel returned '${created}'`), ); messaging().onMessage(async remoteMessage => { var localNotification = { channelId: 'channel-id', channelName: 'My channel', title: remoteMessage.data.title, message: remoteMessage.data.body, userInfo: remoteMessage.data, data: remoteMessage.data, soundName: 'default', onNotification: function (notification) { console.log('onNotification:'); console.log('onNotification:' + notification); }, }; Platform.OS == 'android' && (localNotification = { ...localNotification, channelId: 'channel-id', channelName: 'My channel', largeIcon: '', smallIcon: Platform.Version >= 23 ? 'ic_launcher' : 'ic_launcher_round', priority: 'high', ignoreInForeground: false, onlyAlertOnce: false, invokeApp: true, }); setNotificationData({}); setNotificationData(remoteMessage.data); PushNotification.localNotification(localNotification); }); messaging().setBackgroundMessageHandler(async remoteMessage => { console.log("Background message handler") setNotificationData( remoteMessage.data ); if(auth().currentUser !== null){ const userId = auth().currentUser.uid firestore().collection("users").doc(userId).update({ coins:firestore.FieldValue.increment(1) }).then(res => { console.log("Success update coins") }).catch(err => { console.log("Error update coins") }) } }); messaging().onNotificationOpenedApp(remoteMessage => { console.log("on notification Opened App") setNotificationData( remoteMessage.data ); }); messaging() .getInitialNotification() .then(remoteMessage => { if (remoteMessage) { console.log("Initial Notification") setNotificationData( remoteMessage.data ); } }); }; return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen options={{headerShown: false}} name="Login" component={LoginScreen} /> <Stack.Screen options={{headerShown: false}} name="Home" component={HomeScreen} /> <Stack.Screen options={{headerShown: false}} name="SendNotification" component={SendNotificationScreen} /> </Stack.Navigator> </NavigationContainer> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#ffffff', alignItems: 'center', justifyContent: 'center', }, });
已尝试操作
- 清理npm缓存
- 清理React Native缓存
- 切换Node版本
- 删除并重新安装Pods
- 清理Xcode构建产物
- 按照rnfirebase.io指南配置Firebase
解决方案
1. 修复metro.config.js配置错误
原配置存在导出冲突,导致transformer设置被覆盖,替换为合并后的正确配置:
const { getDefaultConfig } = require("metro-config"); module.exports = (async () => { const { resolver } = await getDefaultConfig(); return { transformer: { getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, inlineRequires: true, }, }), }, resolver: { ...resolver, sourceExts: [...resolver.sourceExts, "cjs"], }, }; })();
2. 移除react-native-push-notification,避免与@react-native-firebase/messaging冲突
@react-native-firebase/messaging已完整支持远程/本地通知处理,无需额外依赖react-native-push-notification,两者共存会导致NativeEventEmitter初始化失败。执行以下步骤:
- 卸载依赖:
npm uninstall react-native-push-notification - 清理iOS目录Pods:
cd ios && rm -rf Pods Podfile.lock && pod install
3. 调整App.js中的通知处理逻辑(移除react-native-push-notification相关代码)
修改后的核心通知处理逻辑如下(仅保留@react-native-firebase/messaging相关代码):
// 移除PushNotification导入 // 替换requestPermission和createNotificationListners函数 const requestPermission = async () => { const oldToken = await messaging().getToken(); console.log('oldToken:' + oldToken); if(auth().currentUser){ firestore().collection("userToken").doc(auth().currentUser.uid).update({ token:oldToken }) } messaging().onTokenRefresh(token => { console.log('tokenRefresh:' + token); firestore().collection("userToken").add({ token:token }) }); const granted = await messaging() .requestPermission() .catch(err => console.log(err)); if (granted) { console.log('User granted messaging permissions!'); createNotificationListners(); } else { console.log('User declined messaging permissions :('); } await messaging().registerDeviceForRemoteMessages(); return granted; }; const createNotificationListners = async () => { messaging().onMessage(async remoteMessage => { console.log("Foreground message received:", remoteMessage.data); setNotificationData(remoteMessage.data); }); messaging().setBackgroundMessageHandler(async remoteMessage => { console.log("Background message handler"); if(auth().currentUser !== null){ const userId = auth().currentUser.uid firestore().collection("users").doc(userId).update({ coins:firestore.FieldValue.increment(1) }).then(res => { console.log("Success update coins") }).catch(err => { console.log("Error update coins") }) } }); messaging().onNotificationOpenedApp(remoteMessage => { console.log("On notification opened app"); setNotificationData(remoteMessage.data); }); messaging() .getInitialNotification() .then(remoteMessage => { if (remoteMessage) { console.log("Initial Notification"); setNotificationData(remoteMessage.data); } }); };
4. 确保Firebase配置文件正确导入
检查Xcode项目中GoogleService-Info.plist的Target membership是否勾选了当前应用目标,确保文件被正确打包进应用。
5. 重新构建项目
- 清除React Native缓存:
npx react-native start --reset-cache - 清理Xcode构建产物:打开Xcode,按
Cmd+Shift+K清理,然后重新运行项目。
内容的提问来源于stack exchange,提问作者Muiez arif
相关产品推荐
相关产品推荐

