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

React Native中Auth Context与Google重定向返回交互异常问题

React Native + Expo Auth Session Google认证问题:重定向后响应丢失

我在React Native项目中使用expo-auth-session库实现Google认证功能,目前遇到的问题是:选择用户并完成授权后,当重定向返回应用时,Google返回的token无法被正确处理。我怀疑问题出在AuthContext的配置上,因为重定向会导致视图重新加载,进而丢失了响应数据。

之所以认定是AuthContext的问题,是因为如果把所有登录逻辑移到主文件app/_layout.tsx中并移除AuthContext,功能就能正常工作,token也能正确返回。奇怪的是,普通登录(与后端交互的逻辑)是正常的,所以我猜测问题和Google的重定向机制有关。

应用目录结构

app/
├── (auth)/
│   └── login.tsx
├── (drawer)/
├── _layout.tsx
AuthContext.tsx
services/
├── AuthService.ts
└── TokenService.ts

代码片段

app/_layout.tsx(入口文件)

function RootLayoutNavigation() {
  const tamaguiConfig = createTamagui(config);

  return (
    <TamaguiProvider config={tamaguiConfig}>
      <Theme name="light">
        <AuthProvider>
          <SafeAreaView style={{ flex: 1 }}>
            <StatusBar barStyle="default" backgroundColor="#f2f2f2" translucent />
            <Slot />
          </SafeAreaView>
        </AuthProvider>
      </Theme>
    </TamaguiProvider>
  );
}

AuthContext.tsx(认证状态管理)

import React, {useContext, createContext, type PropsWithChildren, useEffect, useState} from 'react';
import {loadUser, login, logout} from "~/services/AuthService";
import {useRouter, useSegments} from "expo-router";
import {getToken} from "~/services/TokenService";
import {ActivityIndicator} from "react-native";
import {View} from "tamagui";

type User = any;

interface AuthContextType {
  user: User | null;
  signIn: (userData: User) => void;
  signOut: () => Promise<void>;
  isLoading: boolean;
  isAuthenticated: boolean;
}

const AuthContext = createContext<AuthContextType>({
  user: null,
  signIn: () => {},
  signOut: async () => {},
  isLoading: true,
  isAuthenticated: false,
});

function useProtectedRoute(user: User | null) {
  const segments = useSegments();
  const router = useRouter();
  console.log("segments", segments)

  useEffect(() => {
    if (segments.length === 0) return;

    const inAuthGroup = segments[0] === '(auth)';

    if (!user && !inAuthGroup) {
      console.log("entra 1")
      router.replace('/(auth)/login');
    } else if (user && inAuthGroup) {
      console.log("entra 2")
      router.replace('/(drawer)');
    }

  }, [user, segments]);
}

export function AuthProvider({ children }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  useProtectedRoute(user);

  useEffect(() => {
    async function loadSession() {
      try {
        const token = await getToken();
        if (token) {
          const userData = await loadUser();
          setUser(userData);
          setIsAuthenticated(true);
        }
      } catch (error) {
        console.error('Error loading session:', error);
      } finally {
        setIsLoading(false);
      }
    }

    loadSession();
  }, []);

  const signIn = (userData: User) => {
    setUser(userData);
    setIsAuthenticated(true);
  };

  const signOut = async () => {
    try {
      await logout();
      setUser(null);
      setIsAuthenticated(false);
    } catch (error) {
      console.error('Error during logout:', error);
    }
  };

  return (
    <AuthContext.Provider value={{ user, signIn, signOut, isLoading, isAuthenticated }}>
      {!isLoading ? children : (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
          <ActivityIndicator size="large" />
        </View>
      )}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}

login.tsx(登录页面)

const Login = () => {
  const { signIn } = useAuth();
  const router = useRouter()
  const [isLoadingLogin, setIsLoadingLogin] = useState(false)
  const [email, setEmail] = useState("")
  const [password, setPassword] = useState("")
  const [errors, setErrors] = useState({})

  const config = {
    androidClientId: "client-id",
    webClientId: "client-id 2",
    expoClientId: "client-id 3"
  }

  const [request, response, promptAsync] = Google.useAuthRequest(config);


  React.useEffect(() => {
    handleEffect()
  }, [response])

  async function handleEffect(){
    // 这里一直是null
    console.warn("handle Effect() - token " + response)
  }


  return (
    <SafeAreaView style={{flex: 1, backgroundColor: "#e3e3e3"}}>
        <H4>Log-in</H4>
        <Text>{JSON.stringify(response)}</Text> {/* 这里也一直是null */}

        <View minWidth={350} marginVertical={12} flex={1} gap={10}>
          <View marginBottom={10}>
            <Input
              placeholder="Email"
              value={email}
              onChangeText={(text) => {
                setEmail(text);
              }}
              keyboardType="email-address"
              autoCapitalize="none"
            />
          </View>
          <View>
            <Input
              placeholder="Contraseña"
              value={password}
              onChangeText={setPassword}
              secureTextEntry={true}
              autoCapitalize="none"
            />
          </View>

          <View>
            <Pressable
              onPress={() => promptAsync()}
              style={({ pressed }) => ({
                flexDirection: 'row',
                alignItems: 'center',
                justifyContent: 'center',
                padding: 10,
                borderRadius: 30,
                backgroundColor: pressed ? '#f0f0f0' : '#ffffff',
                borderWidth: 1,
                borderColor: '#dcdcdc',
              })}
            >
              <Text paddingRight={9} fontWeight={600} fontSize={15.5}>
                Entrar con Google
              </Text>
              <Image
                source={require('~/assets/images/google-logo.png')}
                style={{
                  width: 22,
                  height: 22,
                }}
              />
            </Pressable>
          </View>
        </View>
    </SafeAreaView>
  )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:50:58