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
相关产品推荐
相关产品推荐

