React Query SSR模式prefetchQuery未触发请求问题排查
问题根因
SSR预取失效、返回的dehydratedState无法被客户端命中,核心由两个问题导致:
codegen.yml配置语法错误,fetcher相关配置未正确嵌套,导致生成的queryKey、fetcher方法逻辑异常- 服务端预取和客户端调用时生成的queryKey不匹配,hydration阶段无法命中服务端预取的缓存
修复步骤
1. 修正codegen.yml配置
原配置中fetcher字段的嵌套结构错误,graphql-request的端点配置没有正确归属到fetcher配置下,修正后配置如下:
overwrite: true schema: "https://api.spacex.land/graphql/" documents: "./src/graphql/**/*.gql" generates: src/generated.ts: plugins: - typescript - typescript-operations - typescript-react-query config: pureMagicComment: true exposeQueryKeys: true exposeFetcher: true fetcher: func: graphql-request endpoint: "https://api.spacex.land/graphql/" fetchParams: headers: Content-Type: "application/json"
配置修改后重新执行代码生成命令,输出最新的generated.ts文件。
2. 对齐服务端与客户端的queryKey
typescript-react-query生成的getKey()方法会将查询变量作为queryKey的一部分,格式为[查询名称, 变量对象]。原代码中服务端调用getKey()时未传入变量,和客户端传入{limit:5}生成的queryKey完全不一致,无法命中缓存。
同时原代码中存在两个冗余/错误写法:
- 手动给生成的hook传入TS泛型,生成的hook已经内置完整类型,额外传泛型可能导致类型推断异常
- 对dehydrate结果做
JSON.parse(JSON.stringify())序列化,Next.js会自动处理props的序列化,该操作无意义
修正后的页面代码如下:
// index.tsx import type { NextPage } from 'next' import Head from 'next/head' import { QueryClient, dehydrate} from "react-query" import { useGetLaunchesPastQuery } from '../generated'; const Home: NextPage = () => { // 移除多余泛型,传入和服务端预取完全一致的变量 const { data, isFetching } = useGetLaunchesPastQuery({ limit: 5 }); return ( <div className={styles.container}> <Head> <title>Create Next App</title> <meta name="description" content="Generated by create next app" /> <link rel="icon" href="/favicon.ico" /> </Head> <main className={styles.main}> {data?.launchesPast && ( <ul> {data.launchesPast.map((launch) => ( <li key={launch?.mission_name}>{launch?.mission_name} ({launch?.launch_date_local})</li> ))} </ul> )} </main> </div> ); } export const getServerSideProps = async () => { const queryClient = new QueryClient(); // 定义和客户端完全一致的查询变量,保证queryKey对齐 const queryVariables = { limit: 5 }; await queryClient.prefetchQuery( useGetLaunchesPastQuery.getKey(queryVariables), useGetLaunchesPastQuery.fetcher(queryVariables) ); return { props: { dehydratedState: dehydrate(queryClient), }, }; }; export default Home
注意事项
如果服务端预取和客户端使用的查询变量不一致(比如服务端预取limit=10,客户端传limit=5),属于queryKey不匹配的场景,客户端会自动发起新请求,是React Query的预期行为,要实现SSR首屏带数据,必须保证两边查询名称、查询变量完全一致。
内容的提问来源于stack exchange,提问作者wsfuller
相关产品推荐
相关产品推荐

