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

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,避免全局变量的潜在问题:

  1. 创建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);
  1. 在全局错误处理中调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 13:52:27