Next.js中React Query设置staleTime=Infinity仍重复请求问题
在Next.js中使用getServerSideProps进行服务端数据获取时,从页面A跳转至页面B,发现具有相同queryKey的请求被重复发起。已将staleTime设置为Infinity,是否应该从缓存读取API响应?
示例代码:
export const getServerSideProps: GetServerSideProps = async (context) => { await queryClient.fetchQuery({ queryKey: ['posts'], queryFn: () => fetchPosts(), // 从API获取文章的简单函数 staleTime: Infinity, }); return { props: { dehydratedState: dehydrate(queryClient), }, }; }
假设页面B也存在相同的getServerSideProps,期望在页面A与页面B之间切换时,React Query能使用queryKey: ['posts']的缓存数据,而非每次页面切换都重新请求。
问题核心是服务端Query Client实例隔离:每次触发getServerSideProps时,Next.js会创建全新的React Query Client实例,不同页面的SSR请求用的是完全独立的缓存空间,就算设置了staleTime: Infinity,页面A的服务端缓存也无法被页面B的SSR过程读取。
要实现跨页面复用缓存,可从以下方向处理:
客户端缓存优先策略
在客户端组件中使用useQuery并保留staleTime: Infinity,服务端仅做首次数据预取脱水,后续页面切换完全依赖客户端的Query Client缓存(客户端缓存会在页面跳转时保留)。
调整后代码示例:// 服务端getServerSideProps逻辑不变 export const getServerSideProps: GetServerSideProps = async (context) => { const queryClient = new QueryClient(); await queryClient.fetchQuery({ queryKey: ['posts'], queryFn: fetchPosts, staleTime: Infinity }); return { props: { dehydratedState: dehydrate(queryClient) } }; }; // 客户端组件中 const { data } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts, staleTime: Infinity, });共享服务端Query Client实例(需注意隔离)
自定义全局Query Client单例,让所有getServerSideProps复用同一实例,但必须处理用户请求隔离,避免多用户缓存污染。比如结合Next.js的请求上下文存储用户专属缓存:// 创建全局Query Client单例 let queryClient: QueryClient | null = null; export const getQueryClient = () => { if (!queryClient) { queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: Infinity } }, }); } return queryClient; }; // 在getServerSideProps中使用 export const getServerSideProps: GetServerSideProps = async (context) => { const queryClient = getQueryClient(); // 生产环境需在此处添加用户隔离逻辑,比如基于context.req.headers.cookie区分缓存 await queryClient.fetchQuery({ queryKey: ['posts'], queryFn: fetchPosts }); return { props: { dehydratedState: dehydrate(queryClient) } }; };改用静态生成(SSG)
如果数据更新频率低,用getStaticProps+revalidate预生成页面,数据仅在构建或重新验证时请求一次,彻底避免重复请求。
最安全易维护的方案是客户端缓存优先,既保证首屏有预渲染数据,又能在页面切换时复用客户端缓存,避免不必要的服务端请求。
内容的提问来源于stack exchange,提问作者Nirmalya Ghosh

