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
相关产品推荐
相关产品推荐

