NextJS 13页面预渲染报错:Relay查询缺productId参数如何解决
问题背景
在构建pages/productDetail.tsx组件时触发预渲染错误,报错信息如下:
Error occurred prerendering page "/productDetail".
RelayNetwork: No data returned for operationproductStateByKeyQuery, got error(s):
Variableidis required.
开发环境(npm run dev)下,带productId参数的URL能正常加载数据,但构建阶段预渲染时无法获取URL参数,导致Relay查询因缺少必填的id变量失败。
解决方案
方案1:禁用页面预渲染(客户端动态渲染)
通过next/dynamic导入组件并关闭服务端渲染,让页面仅在客户端加载后再执行查询:
// pages/productDetail.tsx import dynamic from 'next/dynamic'; // 动态导入组件,禁用服务端渲染 const ProductDetail = dynamic(() => import('./components/ProductDetail'), { ssr: false, loading: () => <div>Loading...</div> }); export default ProductDetail;
将原组件代码拆分到components/ProductDetail.tsx中(保持原逻辑不变),这样预渲染阶段不会执行Relay查询,客户端加载完成后会获取URL中的productId并执行查询。
方案2:使用getServerSideProps实现服务端渲染
getServerSideProps会在每次请求时运行,可直接获取URL参数,适合需要SEO优化的场景。修改后的组件代码如下:
// pages/productDetail.tsx import React from 'react'; import { usePreloadedQuery, preloadQuery } from 'react-relay'; import { RelayEnvironmentProvider } from 'react-relay'; import environment from '@/relay/environment'; // 你的Relay环境配置 import { productStateByKeyQuery } from '@/queries/productStateByKey'; import type { productStateByKeyQuery as qt } from '@/queries/__generated__/productStateByKeyQuery.graphql'; // 服务端获取参数并预加载查询 export async function getServerSideProps(context) { const productId = context.query.productId; // 无参数时重定向到首页或返回404 if (!productId) { return { redirect: { destination: '/', permanent: false, }, }; } const queryRef = preloadQuery( environment, productStateByKeyQuery, { id: productId } ); return { props: { queryRef } }; } export default function ProductDetail({ queryRef }) { const data = usePreloadedQuery<qt>(productStateByKeyQuery, queryRef); return ( <RelayEnvironmentProvider environment={environment}> <div>ProductDetail {data.productStateByKey?.id}</div> <div>ID: {data.productStateByKey?.id}</div> <div>Version: {data.productStateByKey?.version}</div> <div>Product code: {data.productStateByKey?.product?.code}</div> </RelayEnvironmentProvider> ); }
注意:需确保Relay环境在服务端能正常初始化,避免因环境配置差异导致服务端渲染失败。
方案3:组件内参数判断(有限场景适用)
在组件内判断productId是否存在,仅当参数有效时执行查询:
// pages/productDetail.tsx import { useRouter } from 'next/router'; import React from 'react'; import { useLazyLoadQuery } from 'react-relay'; import { productStateByKeyQuery } from '@/queries/productStateByKey'; import type { productStateByKeyQuery as qt } from '@/queries/__generated__/productStateByKeyQuery.graphql'; export default function ProductDetail() { const router = useRouter(); const productId = router.query.productId; // 仅当productId存在时执行查询 const data = productId ? useLazyLoadQuery<qt>( productStateByKeyQuery, { id: productId }, { fetchPolicy: 'store-or-network' } ) : null; // 未获取到参数时显示加载状态 if (!productId) { return <div>Loading...</div>; } return ( <> <div>ProductDetail {productId}</div> <div>ID: {data?.productStateByKey?.id}</div> <div>Version: {data?.productStateByKey?.version}</div> <div>Product code: {data?.productStateByKey?.product?.code}</div> </> ); }
注意:此方案可能仍会触发预渲染错误,因为Next.js预渲染时router.query为空,useLazyLoadQuery的条件判断可能无法阻止查询执行,仅作为补充方案。
方案选择建议
- 无需SEO优化:优先选方案1,实现简单,避免预渲染问题。
- 需要SEO优化:优先选方案2,通过服务端渲染获取数据并生成可被搜索引擎抓取的页面。
内容的提问来源于stack exchange,提问作者Zoltan Hernyak

