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

React-Query刷新令牌后重取失效查询仍用旧令牌的问题

React Query 重取时使用旧令牌的原因及解决方法

问题原因

你的代码存在闭包捕获旧值的核心问题:

  • callApi 通过 useCallback 创建并依赖 accessToken,当令牌更新时会生成新的 callApi 实例。
  • 但 useGetItems 中的查询函数是 Hook 首次执行时创建的闭包,它捕获了当时的旧 callApi 引用。即使后续令牌更新导致 callApi 刷新,React Query 缓存的查询函数依然持有旧的 callApi,因此重取时会使用旧令牌发起请求。

解决方法

方法一:将令牌加入查询键

把 accessToken 作为查询键的一部分,令牌更新时,React Query 会自动识别为新的查询上下文,重取时使用最新的令牌:

export const useGetItems = () => {
    const [{ accessToken }] = useAuthState();
    const callApi = useAxios()

    return useQuery(
        [ALL_ITEMS_KEY, accessToken], // 加入accessToken到查询键
        () =>
            callApi({
                method: 'GET',
                url: 'api/v1/items',
            }).then((data) => data.data),
    )
}

令牌更新后,查询键变化会触发 React Query 自动重取,且使用最新的 callApi。

方法二:让查询函数依赖最新的 callApi

用 useCallback 包裹查询函数,将 callApi 作为依赖,确保每次 callApi 更新时,查询函数同步更新:

export const useGetItems = () => {
    const callApi = useAxios()

    const fetchItems = useCallback(() => {
        return callApi({
            method: 'GET',
            url: 'api/v1/items',
        }).then((data) => data.data)
    }, [callApi])

    return useQuery(ALL_ITEMS_KEY, fetchItems)
}

当令牌更新导致 callApi 刷新时,fetchItems 会生成新的函数实例,React Query 重取时会使用这个最新的查询函数,从而拿到新令牌。

方法三:用 Ref 实时获取最新令牌

修改 useAxios,使用 useRef 存储令牌,每次调用 callApi 时直接读取最新值,避免闭包捕获旧值:

const useAxios = (baseUrl = config.API_URL) => {
    const history = useHistory()
    const [authState] = useAuthState()
    const accessTokenRef = useRef(authState.accessToken)

    // 实时同步令牌到Ref
    useEffect(() => {
        accessTokenRef.current = authState.accessToken
    }, [authState.accessToken])

    const callApi = useCallback(
        async ({ headers, ...rest }) => {
            try {
                const { data } = await axios({
                    baseURL: baseUrl,
                    headers: {
                        'Content-Type': 'application/json',
                        Authorization: `Bearer ${accessTokenRef.current}`, // 读取最新令牌
                        ...headers,
                    },
                    ...rest,
                    validateStatus: (status) => status >= 200 && status <= 299,
                })
                return data
            } catch (err) {
                if (err?.response?.status === 401) {
                    history.push(`${appConfig.APP_SUBPATH}/sessionExpired`)
                } else if (err?.response?.status === 503) {
                    history.push(`${appConfig.APP_SUBPATH}/underMaintenance`)
                }
                throw err
            }
        },
        [baseUrl, history], // 不再依赖accessToken
    )

    return callApi
}
export default useAxios 

这种方式从根源上解决闭包问题,无论 callApi 是否被缓存,每次调用都会拿到最新的令牌。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 13:14:58