React Native中如何在非组件内删除Token并跳转至登录页
解决React Native非组件中AccessToken刷新失败后的登出跳转问题
方案一:使用全局Navigation Ref
这是最直接的实现方式,通过创建全局导航引用,让非组件代码也能调用导航方法。
- 创建全局导航ref文件
// navigationRef.ts import React from 'react'; import { NavigationContainerRef, StackActions } from '@react-navigation/native'; import { RootStackParamList } from './path-to-your-types'; export const navigationRef = React.createRef<NavigationContainerRef<RootStackParamList>>(); // 封装导航操作方法 export const popToTop = () => { navigationRef.current?.dispatch(StackActions.popToTop()); }; export const navigateToLogin = () => { navigationRef.current?.navigate('LoginScreen'); };
- 在NavigationContainer中绑定ref
// App.tsx import { NavigationContainer } from '@react-navigation/native'; import { navigationRef } from './navigationRef'; function App() { return ( <NavigationContainer ref={navigationRef}> {/* 你的导航栈结构 */} </NavigationContainer> ); }
- 修改deleteTokens方法,移除navigation参数
import { popToTop } from './navigationRef'; const deleteTokens = async () => { await SecureStore.deleteItemAsync('secure_access_token'); await SecureStore.deleteItemAsync('secure_refresh_token'); await SecureStore.deleteItemAsync('secure_user_token'); await SecureStore.deleteItemAsync('secure_deviceid'); await SecureStore.deleteItemAsync('current_curriculum'); popToTop(); // 或者直接调用navigateToLogin()跳转到登录页 };
- 在Apollo RetryLink中直接调用deleteTokens
const linksFrom = from([ new RetryLink({ attempts: async (count, operation, error) => { if (count > 1 && error) { await deleteTokens(); return false; } return refreshLogin() .then(() => false) .catch(async () => { await deleteTokens(); return false; }); }, }), graphqlAuthLink(), uploadLink, ]);
方案二:使用状态管理库(以Zustand为例)
通过状态管理维护登录状态,在根组件监听状态变化执行登出跳转,适合中大型项目的状态统一管理。
- 创建登录状态store
// authStore.ts import { create } from 'zustand'; import * as SecureStore from 'expo-secure-store'; interface AuthState { isLoggedIn: boolean; logout: () => Promise<void>; } export const useAuthStore = create<AuthState>((set) => ({ isLoggedIn: false, logout: async () => { // 删除所有token await SecureStore.deleteItemAsync('secure_access_token'); await SecureStore.deleteItemAsync('secure_refresh_token'); await SecureStore.deleteItemAsync('secure_user_token'); await SecureStore.deleteItemAsync('secure_deviceid'); await SecureStore.deleteItemAsync('current_curriculum'); // 更新登录状态 set({ isLoggedIn: false }); }, }));
- 在根组件监听登录状态变化
// App.tsx import { useAuthStore } from './authStore'; import { NavigationContainer } from '@react-navigation/native'; import { useNavigation, StackActions } from '@react-navigation/native'; function AuthListener() { const isLoggedIn = useAuthStore((state) => state.isLoggedIn); const navigation = useNavigation(); React.useEffect(() => { if (!isLoggedIn) { navigation.dispatch(StackActions.popToTop()); // 或者直接跳转到登录页:navigation.navigate('LoginScreen') } }, [isLoggedIn, navigation]); return null; } function App() { return ( <NavigationContainer> <AuthListener /> {/* 你的导航栈 */} </NavigationContainer> ); }
- 在RetryLink中触发登出
import { useAuthStore } from './authStore'; const linksFrom = from([ new RetryLink({ attempts: async (count, operation, error) => { if (count > 1 && error) { await useAuthStore.getState().logout(); return false; } return refreshLogin() .then(() => false) .catch(async () => { await useAuthStore.getState().logout(); return false; }); }, }), graphqlAuthLink(), uploadLink, ]);
方案三:使用事件发射器
通过事件触发解耦登出逻辑,适合需要在多个非组件位置触发登出的场景。
- 在非组件中触发登出事件
import { DeviceEventEmitter } from 'react-native'; const linksFrom = from([ new RetryLink({ attempts: async (count, operation, error) => { if (count > 1 && error) { DeviceEventEmitter.emit('triggerLogout'); return false; } return refreshLogin() .then(() => false) .catch(() => { DeviceEventEmitter.emit('triggerLogout'); return false; }); }, }), graphqlAuthLink(), uploadLink, ]);
- 在根组件监听事件并执行登出
// App.tsx import { DeviceEventEmitter } from 'react-native'; import { useNavigation, StackActions } from '@react-navigation/native'; import * as SecureStore from 'expo-secure-store'; function App() { const navigation = useNavigation(); React.useEffect(() => { const logoutListener = DeviceEventEmitter.addListener('triggerLogout', async () => { // 删除token await SecureStore.deleteItemAsync('secure_access_token'); await SecureStore.deleteItemAsync('secure_refresh_token'); await SecureStore.deleteItemAsync('secure_user_token'); await SecureStore.deleteItemAsync('secure_deviceid'); await SecureStore.deleteItemAsync('current_curriculum'); // 跳转登录页 navigation.dispatch(StackActions.popToTop()); }); // 清理监听 return () => logoutListener.remove(); }, [navigation]); return ( <NavigationContainer> {/* 你的导航栈 */} </NavigationContainer> ); }
内容的提问来源于stack exchange,提问作者Stephanie Dietrich
相关产品推荐
相关产品推荐

