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

React Query请求遇401时如何全局实现刷新Token自动重试逻辑

React Query 全局处理401自动刷新Token方案

完全不需要逐个给上千个请求加onError逻辑,React Query提供了全局配置入口,一处配置即可对所有query、mutation生效,具体实现如下:

核心实现逻辑

  • 基于React Query全局的QueryCache、MutationCache捕获所有请求错误,无需单独给单个请求配置
  • 增加刷新Token的并发锁,避免多请求同时401时重复发起刷新请求
  • 刷新Token成功后自动重发原请求,刷新失败则清空登录态跳转登录页

具体实现步骤

1. 封装带并发锁的Token刷新方法

锁机制是核心,避免并发重复刷新导致的Token混乱、接口压力问题:

// 全局刷新锁,存储正在执行的刷新请求Promise
let refreshTokenPromise = null

async function getFreshAccessToken() {
  // 已有正在执行的刷新请求时,直接返回该Promise,避免重复请求
  if (refreshTokenPromise) return refreshTokenPromise

  const storedRefreshToken = localStorage.getItem('refreshToken')
  if (!storedRefreshToken) {
    throw new Error('refresh token not exist')
  }

  // 加锁
  refreshTokenPromise = (async () => {
    try {
      const resp = await fetch('/api/auth/refresh', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ refreshToken: storedRefreshToken })
      })
      if (!resp.ok) throw new Error('refresh failed')
      const { accessToken } = await resp.json()
      // 更新本地存储的新Access Token
      localStorage.setItem('accessToken', accessToken)
      return accessToken
    } finally {
      // 无论刷新成功/失败,执行完立即释放锁
      refreshTokenPromise = null
    }
  })()

  return refreshTokenPromise
}

2. 初始化QueryClient时添加全局错误处理

不要在defaultOptions.queries里写onError,直接用QueryCache、MutationCache的全局错误回调,优先级更高,不会被单个请求自定义的onError覆盖:

import { QueryClient, QueryCache, MutationCache } from '@tanstack/react-query'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      // 全局重试规则:仅401错误最多重试1次,避免死循环
      retry: (failureCount, error) => {
        if (failureCount > 1) return false
        return error?.response?.status === 401
      }
    },
    mutations: {
      // mutation和query保持一致的重试规则
      retry: (failureCount, error) => {
        if (failureCount > 1) return false
        return error?.response?.status === 401
      }
    }
  },
  // 全局捕获所有query错误
  queryCache: new QueryCache({
    onError: async (error, failedQuery) => {
      if (error?.response?.status !== 401) return
      try {
        await getFreshAccessToken()
        // 刷新成功后,重发失败的query
        queryClient.invalidateQueries({ queryKey: failedQuery.queryKey })
      } catch (refreshErr) {
        // 刷新失败,清空登录态跳登录页
        localStorage.removeItem('accessToken')
        localStorage.removeItem('refreshToken')
        window.location.replace('/login')
      }
    }
  }),
  // 全局捕获所有mutation错误
  mutationCache: new MutationCache({
    onError: async (error, variables, _, failedMutation) => {
      if (error?.response?.status !== 401) return
      try {
        await getFreshAccessToken()
        // 刷新成功后,用原参数重发失败的mutation
        failedMutation.execute(variables)
      } catch (refreshErr) {
        localStorage.removeItem('accessToken')
        localStorage.removeItem('refreshToken')
        window.location.replace('/login')
      }
    }
  })
})

// 正常用QueryClientProvider包裹根组件即可,所有子组件里的useQuery、useMutation自动继承该配置

3. 统一请求封装注意事项

所有请求发出去前,必须实时从localStorage读取最新的accessToken,不要在封装时缓存初始token值,否则刷新token后还是会携带旧token发起请求:

// 基础请求封装示例
async function request(url, options = {}) {
  const accessToken = localStorage.getItem('accessToken')
  const resp = await fetch(url, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...(options.headers || {}),
      ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {})
    }
  })

  if (!resp.ok) {
    const err = new Error(`Request failed: ${resp.status}`)
    // 把状态码挂载到错误对象上,供React Query识别
    err.response = { status: resp.status }
    throw err
  }
  return resp.json()
}

补充说明

  • 如果你项目里用了axios、fetch wrapper之类的请求库,也可以把401拦截+刷token的逻辑写在请求库的全局拦截器里,和上述React Query全局配置方案二选一即可,两种都不需要修改现有业务请求代码
  • 重试次数必须限制为1次,否则刷新接口本身返回401时会触发无限重试
  • 并发锁逻辑必须加,否则页面同时触发多个401请求时,会瞬间发起多次刷新Token请求,极易导致Token覆盖混乱、刷新接口被打挂

内容的提问来源于stack exchange,提问作者Dmitriy_kzn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 07:39:21