Expo+Firebase JS SDK下RN GoogleSignin模块链接错误及跨平台问题排查
跨平台谷歌登录问题排查(Expo托管工作流 + @react-native-google-signin/google-signin + Firebase)
问题描述
- Web端点击
GoogleSigninButton时抛出错误:Uncaught Error: RN GoogleSignin native module is not correctly linked... - Android模拟器卡在启动页,提示打包完成但无后续页面加载
已执行操作
- 构建开发版
- 安装所有依赖
- 运行
expo doctor无异常提示 - 清理预构建文件
- 启动开发客户端
- 确认配置符合官方文档要求
相关代码配置
SignUpScreen.tsx
import {GoogleSignin,GoogleSigninButton} from '@react-native-google-signin/google-signin'; //... <GoogleSigninButton size={GoogleSigninButton.Size.Wide} color={GoogleSigninButton.Color.Light} // onPress={} //disabled={} style={{ width:'100%', height:SIZES.xxSmall}} />
app.json
"ios": { "config": { "usesNonExemptEncryption": false }, "supportsTablet": true, "bundleIdentifier": "bundleIdentifier", }, "android": { "adaptiveIcon": { "foregroundImage": "./src/assets/adaptive-icon.png", "backgroundColor": "#ffffff" }, "package": "my package" }, "web": { "bundler": "metro", "favicon": "./src/assets/favicon.png" }, "experiments": { "tsconfigPaths": true }, "extra": { "eas": { "projectId": "projectId" } }, "plugins": [ "expo-font", "@react-native-google-signin/google-signin" ], "owner": "owner"
metro.config.ts
const { getDefaultConfig } = require("expo/metro-config"); module.exports = (() => { const config = getDefaultConfig(__dirname); const { transformer, resolver } = config; config.transformer = { ...transformer, babelTransformerPath: require.resolve("react-native-svg-transformer") }; config.resolver = { ...resolver, assetExts: resolver.assetExts.filter((ext:any) => ext !== "svg"), sourceExts: [...resolver.sourceExts, "svg",'cjs','mjs','d.ts', ] }; return config; })();
package.json
dependencies:{ "firebase": "^10.7.0", "@react-native-google-signin/google-signin": "^11.0.0", "@reduxjs/toolkit": "^1.9.7", "expo": "^50.0.0", "expo-auth-session": "~5.4.0", "expo-crypto": "~12.8.0", "expo-dev-client": "~3.3.5", "expo-system-ui": "~2.9.3", "expo-web-browser": "~12.8.1", "react": "^18.2.0", "react-dom": "18.2.0", "react-hook-form": "^7.49.2", "react-native": "^0.73.2",}
GoogleLogin.tsx
import React,{useEffect ,useState} from 'react' import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri } from 'expo-auth-session'; import app,{ auth,provider } from '../../../firebaseConfig'; import { GoogleSignin, statusCodes, GoogleSigninButton,} from '@react-native-google-signin/google-signin'; import {Web_Client_ID } from "../../utils/clientKeys"; const GoogleLogin =() => { const googleSigninConfig = ():void =>{ GoogleSignin.configure({ webClientId:Web_Client_ID , scopes: ["profile", "email"], offlineAccess: true, forceCodeForRefreshToken: true, profileImageSize: 120, }); } const onAuthStateChanged = (user:any)=> { if(user){ console.log(user) }else{ //set user info to null } } useEffect(() => { googleSigninConfig() getCurrentUserInfo() const subscriber = auth().onAuthStateChanged(onAuthStateChanged) //triggered once user either log in or out return subscriber; },[]) const signinWithGoogle = async () => {//ongoogle btn press try { await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true }); const userInfo = await GoogleSignin.signIn(); const {idToken, accessToken} = await GoogleSignin.getTokens() console.log(idToken,accessToken) const credential = firebase.auth.GoogleAuthProvider.credential(idToken, accessToken,); await auth().signInWithCredential(credential); } catch ( error :any) { if (error.code === statusCodes.SIGN_IN_CANCELLED) { console.log('SIGN_IN_CANCELLED') } else if (error.code === statusCodes.IN_PROGRESS) { console.log('IN_PROGRESS') } else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) { console.log('PLAY_SERVICES_NOT_AVAILABLE') } else { // some other error happened } } } //return null if user didnt signin previously const getCurrentUserInfo = async () => { try { const userInfo = await GoogleSignin.signInSilently() console.log(userInfo) } catch ( error :any) { if (error.code === statusCodes.SIGN_IN_REQUIRED) { console.log('SIGN_IN_REQUIRED') } else { console.log('error') } } } const GoogleSignout = async ()=>{ try { await GoogleSignin.revokeAccess() await GoogleSignin.signOut() //.then(() => alert('Your are signed out!')); // setloggedIn(false); } catch ( error :any) { // Alert.alert('Something else went wrong... ', error.toString()) } } }
问题原因与解决方案
1. Web端错误:RN GoogleSignin native module is not correctly linked
@react-native-google-signin/google-signin仅支持React Native原生端(iOS/Android),完全不兼容Web平台。Web端需要单独使用Firebase JS SDK的谷歌登录方案,或结合expo-auth-session实现OAuth登录。
修复步骤:
- 替换Web端登录逻辑,改用Firebase的
signInWithPopup:// Web端专用登录函数 import { getAuth, signInWithPopup, GoogleAuthProvider } from "firebase/auth"; const signInWithGoogleWeb = async () => { const auth = getAuth(); const provider = new GoogleAuthProvider(); try { const result = await signInWithPopup(auth, provider); console.log(result.user); } catch (error) { console.error(error); } }; - 通过平台判断渲染对应按钮:
import { Platform } from 'react-native'; // 组件内渲染逻辑 {Platform.OS === 'web' ? ( <button onClick={signInWithGoogleWeb} style={{width:'100%', padding:10}}>Google登录</button> ) : ( <GoogleSigninButton size={GoogleSigninButton.Size.Wide} color={GoogleSigninButton.Color.Light} onPress={signinWithGoogle} style={{ width:'100%', height:SIZES.xxSmall}} /> )}
2. Android模拟器卡在启动页
可能原因及修复:
- 缺少Android客户端ID:
GoogleSignin.configure中仅配置了webClientId,Android端必须添加androidClientId(从Google Cloud控制台获取):import { Android_Client_ID } from "../../utils/clientKeys"; GoogleSignin.configure({ webClientId: Web_Client_ID, androidClientId: Android_Client_ID, // 添加该行 scopes: ["profile", "email"], offlineAccess: true, forceCodeForRefreshToken: true, profileImageSize: 120, }); - metro配置冗余:
sourceExts中添加的cjs、mjs可能导致依赖解析异常,精简配置:sourceExts: [...resolver.sourceExts, "svg"] - 开发客户端缓存残留:完全卸载模拟器中的开发客户端,重新构建安装:
eas build --profile development --platform android # 将构建后的APK拖入模拟器安装 - Google Play Services版本不兼容:更新模拟器的Google Play Services,或调整最低版本要求:
await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true, minPlayServicesVersion: 12000000 // 根据实际情况调整 });
内容的提问来源于stack exchange,提问作者mermaid
相关产品推荐
相关产品推荐

