Next.js中React Query配置缓存后仍重复调用API问题
解决方案:Next.js getServerSideProps + React Query 重复调用API问题
核心原因
React Query的默认缓存是客户端内存级缓存,而getServerSideProps每次请求都会在服务端独立执行,服务端的查询上下文和客户端完全隔离,所以即使设置了cacheTime和staleTime,服务端渲染阶段仍会触发API调用;如果客户端接管后配置不当,还可能再触发一次重复请求。
1. 服务端预取数据,客户端复用缓存(最优方案)
利用getServerSideProps在服务端提前获取数据,通过initialData传给React Query初始化缓存,彻底避免重复请求:
页面组件代码
import { useGetPolicyPage } from '../hooks/useGetPolicyPage'; // 服务端预取数据 export async function getServerSideProps() { // 和useGetPolicyPage里的API逻辑保持一致 const res = await fetch('https://your-api-domain/refund-policy'); const initialPolicyData = await res.json(); return { props: { initialPolicyData } }; } export default function RefundPolicyPage({ initialPolicyData }) { // 用服务端预取的数据初始化React Query缓存 const { data } = useGetPolicyPage({ initialData: initialPolicyData, staleTime: 86400000, // 1天内数据视为新鲜 cacheTime: 86400000, // 缓存保留1天 }); return <div className="policy-content">{data.content}</div>; }
自定义钩子适配initialData
// hooks/useGetPolicyPage.js import { useQuery } from '@tanstack/react-query'; export function useGetPolicyPage({ initialData, ...options }) { return useQuery({ queryKey: ['refund-policy'], // 固定查询键,确保缓存命中 queryFn: async () => { const res = await fetch('https://your-api-domain/refund-policy'); if (!res.ok) throw new Error('Failed to fetch policy'); return res.json(); }, initialData, ...options, }); }
2. 排查缓存键(queryKey)问题
确保queryKey是固定值(比如['refund-policy']),不要动态生成无意义的变量。如果每次渲染queryKey都不同,React Query会认为是新查询,直接跳过缓存发起请求。
3. 禁用服务端查询(适合非SEO敏感页面)
如果退款政策不需要服务端渲染(比如纯静态内容),可以让React Query仅在客户端执行:
export default function RefundPolicyPage() { const { data } = useGetPolicyPage({ enabled: typeof window !== 'undefined', // 仅在浏览器环境启用查询 staleTime: 86400000, cacheTime: 86400000, }); // 服务端渲染时显示占位内容 if (!data) return <div>Loading refund policy...</div>; return <div className="policy-content">{data.content}</div>; }
4. 检查全局React Query配置
确保_app.js中的Provider没有强制全局刷新的配置,避免覆盖单个查询的staleTime:
// _app.js import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient({ defaultOptions: { queries: { refetchOnMount: false, // 禁止挂载时强制刷新 refetchOnWindowFocus: false, // 禁止窗口聚焦时刷新 staleTime: 3600000, // 全局默认新鲜时间(可选) }, }, }); function MyApp({ Component, pageProps }) { return ( <QueryClientProvider client={queryClient}> <Component {...pageProps} /> </QueryClientProvider> ); } export default MyApp;
内容的提问来源于stack exchange,提问作者Anwar Hossain
相关产品推荐
相关产品推荐

