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

如何用React Query全局处理错误并重取?以过期access token为例

React Query 处理过期 Access Token 的全局解决方案

React Query 完全支持你提出的所有需求,结合它的查询缓存管理、全局错误监听能力,再配合 React Context 就能实现一套完整的过期 token 处理流程。下面是针对你四个需求的具体实现:

核心实现思路

我们会通过监听 React Query 的全局查询错误,在捕获到过期 token 错误(通常对应后端返回的 401 状态码)时,执行以下步骤:

  1. 阻止重复触发刷新逻辑
  2. 取消所有正在运行的查询,避免批量抛出相同错误
  3. 调用刷新 token 的接口更新 Context 中的 token
  4. 失效所有失败/被取消的查询,触发自动重新请求

完整代码示例

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ś

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:27:28