You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native中Firebase+Expo Auth Session谷歌认证加载错误问题

Expo Auth Session谷歌认证错误排查与解决

问题背景

在使用Firebase和Expo Auth Session实现React Native应用的谷歌认证时,遇到如下错误:

Error: Cannot prompt to authenticate until the request has finished loading.

应用结构与流程

  • 核心组件为App和LoginScreen,均通过promptAsyncGoogle函数触发谷歌认证
  • App组件中通过Google.useAuthRequest钩子初始化promptAsyncGoogle,用于应用启动时触发认证
  • LoginScreen组件通过route.params获取该函数,点击“Logga in med Google”按钮时调用

错误现象

  • 直接点击LoginScreen的谷歌登录按钮会触发上述错误
  • 先点击CreateAccount组件(同样使用promptAsyncGoogle)的谷歌登录按钮后,LoginScreen的认证功能恢复正常

相关代码

App组件代码

// Code from App.js

// ...

useEffect(() => {
  const { request, response, promptAsyncGoogle } = Google.useAuthRequest({
    useProxy: true,
    expoClientId: '',
    androidClientId: '',
  });

  // ...

  useEffect(() => {
    if (response?.type === "success") {
      // Handle successful authentication
    } else if (response?.type === "dismiss" || response?.type === "error") {
      // Handle dismissal or error
    }
    
  }, [response]);

  // ...

}, []);

LoginScreen组件代码

const LoginScreen = ({ route }) => {
const { setInfoEntered, promptAsyncGoogle, setLoading, setUser } = route.params

//..

<TouchableOpacity style={styles.googleButton} onPress={() => { promptAsyncGoogle()}}>
      <View style={styles.buttonIcon}>
        <AntDesign name="google" style={styles.googleIcon} />
      </View>
      <Text style={styles.googleText}>Logga in med Google</Text>
</TouchableOpacity>

问题解答

1. LoginScreen组件中出现该错误的原因是什么?

Google.useAuthRequest钩子初始化时,需要先完成认证请求的加载过程(生成有效的请求参数),才能安全调用promptAsyncGoogle。你在App组件的useEffect中初始化该钩子,但没有等待request对象完全就绪,就将promptAsyncGoogle传递给了LoginScreen。此时用户点击按钮调用函数时,请求仍处于未加载完成的状态,因此抛出该错误。

2. 为何先在CreateAccount组件中使用谷歌认证后,LoginScreen的认证才能正常工作?

当你在CreateAccount组件中调用promptAsyncGoogle时,实际触发了请求的加载流程(或者此时request刚好完成加载)。一旦请求加载完成,promptAsyncGoogle就处于就绪状态,后续在LoginScreen中调用同一个函数时,就不会再触发未加载的错误。

3. 如何确保LoginScreen的promptAsyncGoogle函数从一开始就能正常工作,无需先在其他组件中使用?

可以通过以下几种方案解决:

方案1:等待request就绪后再传递函数

在App组件中,使用状态变量存储promptAsyncGoogle,仅当request对象就绪后再更新状态,确保传递给子组件的是已就绪的函数:

// App.js 修改部分
import { useState, useEffect } from 'react';

const App = () => {
  const [promptAsyncGoogle, setPromptAsyncGoogle] = useState(null);

  useEffect(() => {
    const { request, response, promptAsyncGoogle: tempPrompt } = Google.useAuthRequest({
      useProxy: true,
      expoClientId: 'YOUR_CLIENT_ID',
      androidClientId: 'YOUR_ANDROID_CLIENT_ID',
    });

    // 当request存在时,说明请求已加载完成
    if (request) {
      setPromptAsyncGoogle(tempPrompt);
    }

    // 处理response逻辑
    useEffect(() => {
      if (response?.type === "success") {
        // 处理认证成功逻辑
      } else if (response?.type === "dismiss" || response?.type === "error") {
        // 处理取消或错误逻辑
      }
    }, [response]);

  }, []);

  // 导航到LoginScreen时,仅传递已就绪的函数
  navigation.navigate('LoginScreen', {
    setInfoEntered,
    promptAsyncGoogle,
    setLoading,
    setUser
  });
};

方案2:在LoginScreen中禁用按钮直到函数就绪

在LoginScreen中,检查promptAsyncGoogle是否存在,不存在时禁用登录按钮,避免用户提前点击:

// LoginScreen.js 修改部分
<TouchableOpacity 
  style={[styles.googleButton, !promptAsyncGoogle && styles.disabledButton]} 
  onPress={() => { promptAsyncGoogle()}}
  disabled={!promptAsyncGoogle}
>
  <View style={styles.buttonIcon}>
    <AntDesign name="google" style={styles.googleIcon} />
  </View>
  <Text style={styles.googleText}>Logga in med Google</Text>
</TouchableOpacity>

// 可添加禁用样式
const styles = StyleSheet.create({
  // ...其他样式
  disabledButton: {
    opacity: 0.5,
  }
});

方案3:使用React Context全局管理认证逻辑

将谷歌认证的请求和函数抽离到React Context中,让所有需要的组件从Context中获取已就绪的promptAsyncGoogle,避免通过路由参数传递的时机问题:

// 创建AuthContext.js
import { createContext, useContext, useState, useEffect } from 'react';
import * as Google from 'expo-auth-session/providers/google';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [promptAsyncGoogle, setPromptAsyncGoogle] = useState(null);
  const [request, response, promptAsync] = Google.useAuthRequest({
    useProxy: true,
    expoClientId: 'YOUR_CLIENT_ID',
    androidClientId: 'YOUR_ANDROID_CLIENT_ID',
  });

  useEffect(() => {
    if (request) {
      setPromptAsyncGoogle(promptAsync);
    }
  }, [request]);

  // 处理response逻辑
  useEffect(() => {
    if (response?.type === "success") {
      // 处理认证成功逻辑
    } else if (response?.type === "dismiss" || response?.type === "error") {
      // 处理取消或错误逻辑
    }
  }, [response]);

  return (
    <AuthContext.Provider value={{ promptAsyncGoogle }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

然后在App.js中包裹组件:

import { AuthProvider } from './AuthContext';

const App = () => {
  return (
    <AuthProvider>
      {/* 你的导航组件或根组件 */}
    </AuthProvider>
  );
};

最后在LoginScreen中使用:

import { useAuth } from './AuthContext';

const LoginScreen = () => {
  const { promptAsyncGoogle } = useAuth();
  // ...其他逻辑

  return (
    <TouchableOpacity 
      style={[styles.googleButton, !promptAsyncGoogle && styles.disabledButton]} 
      onPress={() => { promptAsyncGoogle()}}
      disabled={!promptAsyncGoogle}
    >
      {/* 按钮内容 */}
    </TouchableOpacity>
  );
};

内容的提问来源于stack exchange,提问作者LetItBurn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 21:45:24