React Query重复发起请求未使用缓存问题排查
React Query 缓存未命中问题:下滑滑块时重复请求相同URL数据
问题背景
开发了一个带滑块的React应用,用于展示视频缩略图,滑块具备「上滑」「下滑」按钮切换内容。因接口限制单次最多返回25条数据,滑块会按需触发新请求。选用React Query是看中其缓存能力,期望上滑请求的数据,下滑时直接调用缓存无需重复请求,但实际下滑时仍会重新发起请求。
相关配置与代码
1. 自定义useQueryClient钩子(QueryClient默认配置)
const useQueryClient = () => { const { showError, completeLoading } = useDispatchAction(); const queryErrorHandler = (err: unknown): void => { const axiosError = err as AxiosError; completeLoading(); showError({ isError: true, errorMessage: axiosError.message }); }; const defaultQueryClientOptions = { queries: { onError: queryErrorHandler, retry: false, cacheTime: 1.1 * (60 * 1000), refetchOnMount: false, refetchOnWindowFocus: false, staleTime: 1 * (60 * 1000), refetchInterval: 1 * (60 * 1000), }, }; return new QueryClient({ defaultOptions: defaultQueryClientOptions, }); }; export default useQueryClient;
2. TanstackQueryProvider组件
import { QueryClientProvider } from "@tanstack/react-query"; import { useDispatchAction, useQueryClient } from "hooks"; import { QueryClient } from "@tanstack/react-query"; import React from "react"; import { ReactNode } from "react"; import { AxiosError } from "axios"; export const TanstackQueryProvider: React.FC<{ children: ReactNode }> = ({ children }) => { // const queryClient = useQueryClient(); const { showError, completeLoading } = useDispatchAction(); const queryErrorHandler = (err: unknown): void => { const axiosError = err as AxiosError; completeLoading(); showError({ isError: true, errorMessage: axiosError.message }); }; const defaultQueryClientOptions = { queries: { onError: queryErrorHandler, retry: false, cacheTime: 1.1 * (60 * 1000), refetchOnMount: false, refetchOnWindowFocus: false, staleTime: 1 * (60 * 1000), refetchInterval: 1 * (60 * 1000), }, }; const [queryClient] = React.useState( () => new QueryClient({ defaultOptions: defaultQueryClientOptions, }) ); return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; }; export default TanstackQueryProvider;
3. 应用入口文件
const container = document.getElementById("root"); const root = createRoot(container as Element); root.render( <AppProvider> <TanstackQueryProvider> <App /> </TanstackQueryProvider> </AppProvider> );
4. 查询逻辑代码
const { isLoading, data, refetch, isFetching, isPreviousData } = useQuery({ queryKey: ["users", url], queryFn: () => fetchThumbnails(url), enabled: Boolean(url), keepPreviousData: true, });
5. 实际请求函数
export const fetchThumbnails = async (url: string) => { const response = await fetch(url); if (!response.ok) { const status = response.status; const result = await response.json(); throw new Error(`${status} ${result?.error?.message || "unknown error"}`); } const data = response.json(); console.log("url", url); return data; };
核心疑问
已确认上下滑动时的URL完全相同,理论上应匹配同一缓存键,但为何未命中缓存?
问题排查与解决方案
1. 移除自动刷新配置
默认配置中设置了refetchInterval: 1 * (60 * 1000),该配置会让React Query每隔60秒自动重新请求数据,即使数据处于新鲜期(staleTime内)也会触发请求。若无需定时刷新,直接删除该配置即可。
2. 修复请求函数的异步逻辑
请求函数中response.json()未添加await,会导致返回Promise而非实际数据,可能引发缓存解析异常。修正代码:
export const fetchThumbnails = async (url: string) => { const response = await fetch(url); if (!response.ok) { const status = response.status; const result = await response.json(); throw new Error(`${status} ${result?.error?.message || "unknown error"}`); } const data = await response.json(); // 添加await console.log("url", url); return data; };
3. 确认queryKey的绝对一致性
即使URL看起来相同,需检查是否存在编码差异、空格、参数顺序不一致等情况。可在组件内打印queryKey的字符串化结果,对比上下滑时的缓存键是否完全一致。
4. 验证QueryClient实例唯一性
确保整个应用中只有一个QueryClient实例,可在TanstackQueryProvider创建实例时添加日志,确认实例未被重复创建。
5. 借助DevTools查看缓存状态
安装React Query DevTools,直观查看缓存中的queryKey及对应数据状态,确认相同queryKey是否存在有效缓存,以及是否被标记为需重新请求。
内容的提问来源于stack exchange,提问作者Kiszuriwalilibori
相关产品推荐
相关产品推荐

