Next.js中getServerSideProps调用prefetchInfiniteQuery返回undefined正常吗
问题原因
prefetchInfiniteQuery返回undefined属于正常设计:该方法为无返回值的异步方法,仅负责将请求到的数据写入QueryClient缓存,不会直接返回查询结果。如果需要同时获取预取结果,可替换为fetchInfiniteQuery方法。- 你当前的代码未完整实现React Query的SSR hydration逻辑,存在三处核心错误:
_app.js中全局声明QueryClient实例,服务端渲染时会在所有请求间共享实例,导致数据串用、内存泄漏问题,且未添加Hydrate组件注入脱水后的缓存数据。getServerSideProps中未实际调用prefetchInfiniteQuery将数据写入缓存,而是手动拼接了providers分页结构,没有走React Query的缓存逻辑。- 未将
dehydrate(queryClient)生成的脱水状态作为根级props返回给页面,导致客户端无法拿到服务端预取的缓存数据。
解决方案
1. 修正_app.js的配置
将QueryClient实例改为组件内部初始化,新增Hydrate组件注入服务端返回的缓存状态:
import { QueryClientProvider, QueryClient, Hydrate } from 'react-query'; import { useState } from 'react'; MyApp ({ Component, pageProps, user_account_menu, userProfile, token, fallback }){ // 保证每个请求/客户端实例拥有独立的QueryClient const [queryClient] = useState(() => new QueryClient()) return ( <> <DefaultSeo {...SEO}/> <FacebookPixel /> <SWRConfig value={{ fallback }}> <ThemeProvider theme={theme}> <GlobalStyles /> <BreakPointProvider> <AuthProvider> <DisplayProvider> <EcommerceProvider> <QueryClientProvider client={queryClient}> {/* 注入服务端预取的缓存数据 */} <Hydrate state={pageProps.dehydratedState}> <Header principal_menu={fallback.principal_menu} menu_sections={fallback.menu_sections} user_account_menu={user_account_menu} token={token} userProfile={userProfile} /> <Component {...pageProps} token={token} reviews={fallback.reviews} newsletter={fallback.newsletter}/> </Hydrate> </QueryClientProvider> <PopUpNewsletter newsletter={fallback.newsletter} /> <Footer footer_menu={fallback.footer_menu} reviews={fallback.reviews} policies={fallback.policies_menu}/> </EcommerceProvider> </DisplayProvider> </AuthProvider> </BreakPointProvider> </ThemeProvider> </SWRConfig> </> ) }
2. 修正getServerSideProps的预取逻辑
调用prefetchInfiniteQuery将第一页数据写入缓存,将脱水后的缓存状态作为props返回:
export async function getServerSideProps(ctx) { const queryClient = new QueryClient() const { query } = ctx; const { page = 1 } = query; const token = parseCookies(ctx).jwt ? parseCookies(ctx).jwt : null const limit = 16; const start = +page === 1 ? 0 : (+page - 1) * limit const configSimple = 'simple' // 预取无限查询第一页数据,queryKey必须和客户端useInfiniteQuery的queryKey完全一致 await queryClient.prefetchInfiniteQuery( ['providers', token], // 替换为你客户端实际使用的queryKey ({ pageParam = 0 }) => { if(!token){ return fetchAPI(getTalentCards); }else{ return fetchAuthorizationAPI(getTalentCards, token, configSimple); } }, { // 配置项和客户端useInfiniteQuery的配置保持一致即可 staleTime: 1000 * 60 * 5, } ) // 其他普通接口预取用prefetchQuery即可,示例: const providerTypesData = await queryClient.fetchQuery(['provider_types'], () => fetchAPI(/* 你的请求地址 */)) const countriesData = await queryClient.fetchQuery(['countries'], () => fetchAPI(/* 你的请求地址 */)) const citiesData = await queryClient.fetchQuery(['cities'], () => fetchAPI(/* 你的请求地址 */)) const providerStatusesData = await queryClient.fetchQuery(['provider_statuses'], () => fetchAPI(/* 你的请求地址 */)) return { props: { dehydratedState: dehydrate(queryClient), // 核心:返回脱水后的缓存 page: +page, provider_types: providerTypesData, countries: countriesData, cities: citiesData, provider_statuses: providerStatusesData, start, limit, token: token } } }
3. 客户端侧适配
确保页面中useInfiniteQuery的queryKey、查询函数、配置项和服务端预取时的参数完全一致,不需要额外接收props里的providers数据,初始化即可直接拿到服务端预取的第一页数据。
内容的提问来源于stack exchange,提问作者taurOan
相关产品推荐
相关产品推荐

