如何用React Query全局处理错误并重取?以过期access token为例
React Query 处理过期 Access Token 的全局解决方案
React Query 完全支持你提出的所有需求,结合它的查询缓存管理、全局错误监听能力,再配合 React Context 就能实现一套完整的过期 token 处理流程。下面是针对你四个需求的具体实现:
核心实现思路
我们会通过监听 React Query 的全局查询错误,在捕获到过期 token 错误(通常对应后端返回的 401 状态码)时,执行以下步骤:
- 阻止重复触发刷新逻辑
- 取消所有正在运行的查询,避免批量抛出相同错误
- 调用刷新 token 的接口更新 Context 中的 token
- 失效所有失败/被取消的查询,触发自动重新请求
完整代码示例
1. 实现 AuthContext 统一管理 Token 状态
import { createContext, useContext, useState } from 'react'; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { // 从本地存储初始化 token const [accessToken, setAccessToken] = useState(localStorage.getItem('accessToken')); // 替换为你的真实刷新 token 接口逻辑 const refreshAccessToken = async () => { try { const response = await fetch('/api/refresh-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken: localStorage.getItem('refreshToken') }), }); if (!response.ok) throw new Error('刷新 token 失败'); const data = await response.json(); const newToken = data.accessToken; setAccessToken(newToken); localStorage.setItem('accessToken', newToken); return newToken; } catch (err) { // 刷新失败后的降级处理,比如清除本地存储并跳转登录页 localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); window.location.href = '/login'; throw err; } }; return ( <AuthContext.Provider value={{ accessToken, refreshAccessToken }}> {children} </AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext);
2. 在根组件中实现全局错误处理逻辑
import { QueryClient, QueryClientProvider, useQueryClient } from '@tanstack/react-query'; import { AuthProvider, useAuth } from './AuthContext'; import { useState, useEffect } from 'react'; // 初始化 QueryClient,关闭默认重试避免 token 过期时重复请求 const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 0, }, }, }); function AppContent() { const queryClient = useQueryClient(); const { refreshAccessToken } = useAuth(); // 标记是否正在刷新 token,防止并发触发 const [isRefreshing, setIsRefreshing] = useState(false); useEffect(() => { // 监听 QueryCache 的错误事件 const unsubscribe = queryClient.getQueryCache().subscribe((event) => { // 根据后端约定判断是否为过期 token 错误 if (event.type === 'error' && event.error.response?.status === 401) { if (isRefreshing) return; setIsRefreshing(true); // 取消所有正在运行的查询,避免批量报错 queryClient.cancelQueries({ predicate: (query) => query.state.status === 'loading', }); // 刷新 token 后,失效并重新获取失败/被取消的查询 refreshAccessToken() .then(() => { queryClient.invalidateQueries({ predicate: (query) => query.state.status === 'error' || query.state.status === 'cancelled', }); }) .finally(() => { setIsRefreshing(false); }); } }); // 组件卸载时取消监听 return unsubscribe; }, [queryClient, refreshAccessToken, isRefreshing]); return ( {/* 放置你的应用路由/页面组件 */} <div>Your App Content</div> ); } export default function App() { return ( <AuthProvider> <QueryClientProvider client={queryClient}> <AppContent /> </QueryClientProvider> </AuthProvider> ); }
关键细节说明
- 全局错误捕获:通过
queryClient.getQueryCache().subscribe监听所有查询的错误事件,精准匹配过期 token 错误。 - 查询取消:
cancelQueries方法通过筛选条件终止所有正在加载的查询,避免同一时间批量抛出 401 错误。 - Token 更新:借助 AuthContext 统一管理 token 状态,刷新成功后所有依赖 token 的请求会自动使用新值。
- 自动重新请求:
invalidateQueries标记目标查询为"失效",React Query 会自动重新发起这些请求,此时请求会携带新的 access token。
内容的提问来源于stack exchange,提问作者Piotr Witkoś
相关产品推荐
相关产品推荐

