React Query全局错误处理:如何避免401错误时重复弹出Toast
解决React Query并发401错误重复触发重定向与Toast的问题
问题描述
我有一个使用React和React Query调用外部API的项目,采用了全局错误处理方案,该方案能处理大部分错误。但当服务器返回401未授权错误(例如令牌被撤销)时,我会重定向到登录页并弹出Toast提示用户重新认证。问题在于首页有多个并发请求同时获取不同数据,它们均返回401,导致重定向时弹出3次Toast。如何实现仅重定向一次并弹出一次Toast?
当前代码示例
const queryClient = new QueryClient({ queryCache: new QueryCache({ onError: (error) => { if (error?.request?.status === 401) { removeCookies(); return toast.info("Please authenticate"); } }, })
解决方案
核心思路:添加全局状态标记,避免重复处理
通过一个标记变量记录是否已经触发过401处理逻辑,确保同一时间段内只执行一次重定向和Toast提示。
方案一:使用全局变量快速实现
// 定义全局标记,初始为false let isHandling401 = false; const queryClient = new QueryClient({ queryCache: new QueryCache({ onError: (error) => { if (error?.request?.status === 401 && !isHandling401) { isHandling401 = true; removeCookies(); toast.info("请重新认证"); // 添加重定向逻辑,比如react-router的navigate // navigate('/login'); // 3秒后重置标记,避免后续真实的新401请求被拦截 setTimeout(() => { isHandling401 = false; }, 3000); } }, })
方案二:React Context管理状态(更符合React生态)
如果项目使用Context,可将标记状态放入Context,避免全局变量的潜在问题:
- 创建AuthErrorContext
import { createContext, useContext, useState } from 'react'; const AuthErrorContext = createContext(); export const AuthErrorProvider = ({ children }) => { const [isHandling401, setIsHandling401] = useState(false); const handle401 = () => { if (!isHandling401) { setIsHandling401(true); removeCookies(); toast.info("请重新认证"); // navigate('/login'); setTimeout(() => { setIsHandling401(false); }, 3000); } }; return ( <AuthErrorContext.Provider value={{ handle401 }}> {children} </AuthErrorContext.Provider> ); }; export const useAuthError = () => useContext(AuthErrorContext);
- 在全局错误处理中调用Context方法
const App = () => { const { handle401 } = useAuthError(); const queryClient = new QueryClient({ queryCache: new QueryCache({ onError: (error) => { if (error?.request?.status === 401) { handle401(); } }, }) }); return ( <QueryClientProvider client={queryClient}> {/* 其他组件内容 */} </QueryClientProvider> ); };
额外优化:取消后续并发请求
处理第一个401后,可通过React Query的cancelQueries方法取消所有正在进行的请求,从根源减少重复触发:
if (error?.request?.status === 401 && !isHandling401) { isHandling401 = true; removeCookies(); toast.info("请重新认证"); navigate('/login'); // 取消所有正在进行的查询请求 queryClient.cancelQueries(); setTimeout(() => { isHandling401 = false; }, 3000); }
内容的提问来源于stack exchange,提问作者Enzo
相关产品推荐
相关产品推荐

