Expo集成Firebase电话认证遇NOBRIDGE错误:RNFBAppModule缺失
问题:Expo React Native集成Firebase电话认证时RNFBAppModule未找到错误
错误信息
ERROR Error: Native module RNFBAppModule not found. Re-check module install, linking, configuration, build and install steps.
环境信息
Expo SDK:"~52.0.23", "react": "18.3.1", "react-native": "0.76.5", "@react-native-firebase/app": "^21.6.1", "@react-native-firebase/auth": "^21.6.1", Platform: iOS/Android
已尝试操作
- 执行命令安装Firebase:
npx expo install @react-native-firebase/app - 在app.json中添加配置:
{ "expo": { "plugins": [ "@react-native-firebase/app" ] } }
- 配置firebase-config.js并填入正确凭据
登录页面代码
import React, { useState } from 'react'; import { View, Text, TextInput, TouchableOpacity, SafeAreaView, StyleSheet, KeyboardAvoidingView, Platform, Keyboard, TouchableWithoutFeedback, Alert } from 'react-native'; import { Phone, Package, ArrowRight } from 'lucide-react-native'; import { useUser } from './context/UserContext'; import { auth } from '../firebase.config'; import { PhoneAuthProvider, signInWithCredential } from 'firebase/auth'; const LoginScreen = () => { const [phoneNumber, setPhoneNumber] = useState(''); const [otpSent, setOtpSent] = useState(false); const [otp, setOtp] = useState(''); const [error, setError] = useState(''); const [verificationId, setVerificationId] = useState(null); const { updateUserData } = useUser(); const handleSendOTP = async () => { try { const phoneRegex = /^[0-9]{10}$/; if (!phoneRegex.test(phoneNumber)) { setError('请输入有效的10位手机号码'); return; } const formattedPhone = `+91${phoneNumber}`; // 调整国家代码 const appVerifier = { type: 'recaptcha', verify: () => Promise.resolve('dummy-token') }; const verificationId = await PhoneAuthProvider.verifyPhoneNumber( auth, formattedPhone, appVerifier ); setVerificationId(verificationId); setOtpSent(true); setError(''); Alert.alert('OTP已发送', '请查看手机获取验证码'); } catch (err) { setError('发送OTP失败,请重试。'); console.error('Send OTP error:', err); } }; const handleVerifyOTP = async () => { try { if (otp.length !== 6) { setError('请输入6位OTP验证码'); return; } const credential = PhoneAuthProvider.credential(verificationId, otp); const userCredential = await signInWithCredential(auth, credential); updateUserData({ phone: phoneNumber, isLoggedIn: true, uid: userCredential.user.uid }); setError(''); } catch (err) { setError('OTP验证码无效,请重试。'); console.error('Verify OTP error:', err); } }; return ( <SafeAreaView style={styles.container}> <TouchableWithoutFeedback onPress={Keyboard.dismiss}> <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={styles.keyboardContainer} > <View style={styles.header}></View> <View style={styles.content}> <View style={styles.logoContainer}> <Package color="#FF85A2" size={64} /> <Text style={styles.welcomeText}>欢迎使用Pink Parcel</Text> <Text style={styles.subtitleText}>您的优质配送伙伴</Text> </View> {!otpSent ? ( <View style={styles.inputSection}> <View style={styles.inputContainer}> <Phone color="#FF85A2" size={24} style={styles.inputIcon} /> <TextInput placeholder="输入手机号码" placeholderTextColor="#8F90A6" keyboardType="phone-pad" value={phoneNumber} onChangeText={setPhoneNumber} maxLength={10} style={styles.input} /> </View> <TouchableOpacity style={styles.primaryButton} onPress={handleSendOTP} > <Text style={styles.buttonText}>发送OTP</Text> <ArrowRight color="#FFFFFF" size={20} /> </TouchableOpacity> </View> ) : ( <View style={styles.inputSection}> <View style={styles.inputContainer}> <Package color="#E91E63" size={24} style={styles.inputIcon} /> <TextInput placeholder="输入6位OTP验证码" placeholderTextColor="#8F90A6" keyboardType="number-pad" value={otp} onChangeText={setOtp} maxLength={6} style={styles.input} /> </View> <TouchableOpacity style={styles.primaryButton} onPress={handleVerifyOTP} > <Text style={styles.buttonText}>验证OTP</Text> <ArrowRight color="#FFFFFF" size={20} /> </TouchableOpacity> <TouchableOpacity style={styles.secondaryButton} onPress={() => setOtpSent(false)} > <Text style={styles.secondaryButtonText}>更换手机号码</Text> </TouchableOpacity> </View> )} {error ? ( <Text style={styles.errorText}>{error}</Text> ) : null} </View> </KeyboardAvoidingView> </TouchableWithoutFeedback> </SafeAreaView> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#FFF5F8', }, keyboardContainer: { flex: 1, }, header: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', paddingHorizontal: 20, paddingVertical: 60, backgroundColor: '#FFF5F8', borderBottomWidth: 1, borderBottomColor: '#FFF5F8', }, headerTitle: { fontSize: 20, fontWeight: '700', color: '#2D2D3A', }, content: { flex: 1, padding: 20, }, logoContainer: { alignItems: 'center', marginVertical: 40, }, welcomeText: { fontSize: 24, fontWeight: '700', color: '#2D2D3A', marginTop: 16, marginBottom: 8, }, subtitleText: { fontSize: 16, color: '#8F90A6', }, inputSection: { backgroundColor: '#FFFFFF', borderRadius: 24, padding: 20, shadowColor: '#E91E63', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.08, shadowRadius: 12, elevation: 5, }, inputContainer: { flexDirection: 'row', alignItems: 'center', borderBottomWidth: 1, borderBottomColor: '#FFE4EC', marginBottom: 20, paddingVertical: 8, }, inputIcon: { marginRight: 12, }, input: { flex: 1, height: 50, fontSize: 16, color: '#2D2D3A', }, primaryButton: { backgroundColor: '#E91E63', borderRadius: 16, paddingVertical: 14, alignItems: 'center', flexDirection: 'row', justifyContent: 'center', gap: 8, }, buttonText: { color: '#FFFFFF', fontSize: 15, fontWeight: '600', }, secondaryButton: { paddingVertical: 14, alignItems: 'center', marginTop: 12, }, secondaryButtonText: { color: '#E91E63', fontSize: 15, fontWeight: '600', }, errorText: { color: '#E91E63', textAlign: 'center', marginTop: 16, fontSize: 14, }, }); export default LoginScreen;
预期与实际行为
- 预期:Firebase电话认证功能正常运行
- 实际:出现NOBRIDGE错误,提示原生模块未找到
解决方案
1. 适配Expo工作流
@react-native-firebase依赖原生模块,Expo管理工作流默认不支持第三方原生模块,需转为裸工作流:
npx expo prebuild
预构建后会生成iOS/Android原生目录,后续需用Xcode/Android Studio构建项目,而非直接使用expo start。
2. 完善Firebase插件配置
app.json中需补充Firebase项目凭据,仅添加插件名无法完成原生配置:
{ "expo": { "plugins": [ [ "@react-native-firebase/app", { "googleServicesFile": "./google-services.json", // Android配置文件 "googleServicesPlist": "./GoogleService-Info.plist" // iOS配置文件 } ], "@react-native-firebase/auth" ] } }
确保google-services.json和GoogleService-Info.plist已放置在项目根目录,文件从Firebase控制台下载。
3. 清理缓存并重新安装依赖
- 删除依赖目录和锁文件:
rm -rf node_modules package-lock.json yarn.lock
- 重新安装依赖:
npm install
- 清除Expo缓存:
npx expo start --clear
4. 确认认证模块安装
电话认证需单独安装auth模块:
npx expo install @react-native-firebase/auth
安装后确保插件已添加到app.json的plugins数组中。
5. 修复App Verifier配置
代码中的dummy verifier仅适用于测试,生产环境需配置真实的Recaptcha:
- 导入
RecaptchaVerifier:
import { RecaptchaVerifier } from '@react-native-firebase/auth';
- 在组件中初始化Recaptcha:
useEffect(() => { const appVerifier = new RecaptchaVerifier('recaptcha-container', {}, auth); setAppVerifier(appVerifier); }, []);
- 在界面中添加空View用于渲染Recaptcha:
<View id="recaptcha-container" />
内容的提问来源于stack exchange,提问作者Ramanjit Singh
相关产品推荐
相关产品推荐

