Next.js嵌套动态路由跳转偶现undefined参数问题排查
问题背景
项目目录结构
shop - [categorySlug] - [productSlug] - index.tsx - index.tsx index.tsx
正常路由示例
/shop/shoes/nike-1/shop/jersey/jersey-1
问题复现代码
[categorySlug]目录下的index.tsx代码如下:
export function ShopCategoryComponent({ products }): JSX.Element { const { query } = useRouter(); const router = useRouter(); const categorySlug = hasCategorySlug(query) ? query.categorySlug : undefined; const viewProductDeatails = useCallback( (productSlug) => { router.push(`/shop/${categorySlug}/${productSlug}`); }, [router] ); return ( <section> {products.map((product) => ( <div key={`${product.id}`} onClick={() => viewProductDeatails(product.slug)} > <h3>{product.name}</h3> <h3>{product?.$on['SimpleProduct'].price()}</h3> <img src={product?.$on['SimpleProduct'].image.sourceUrl()} /> </div> ))} </section> ); } export default function ShopCategory() { const { useQuery } = client; const { query } = useRouter(); const { products } = useQuery(); const categorySlug = hasCategorySlug(query) ? query.categorySlug : undefined; const generalSettings = useQuery().generalSettings; const setProducts = products({ where: { categoryIn: categorySlug, }, }).nodes; return ( <React.Fragment> <Menu /> <main className='content content-single'> <ShopCategoryComponent products={setProducts} /> </main> <Footer copyrightHolder={generalSettings.title} /> </React.Fragment> ); }
预期逻辑:点击商品卡片/图片后,跳转到对应[productSlug]的商品详情页。实际偶发跳转异常,路径为/shop/shoes/undefined。
错误原因
一共有两处逻辑问题,第二点是直接触发该异常的核心原因:
useCallback依赖缺失,存在闭包陷阱viewProductDeatails函数内部使用了categorySlug变量,但useCallback的依赖数组只传入了router,没有把categorySlug加入依赖。Next.js客户端水合初期,useRouter()返回的query是空对象,此时categorySlug值为undefined,useCallback会缓存这个初始状态生成的函数;等后续路由参数解析完成,因为没有声明对应依赖,缓存的函数不会更新,会触发/shop/undefined/xxx类的跳转异常。- 异步数据无加载兜底、字段取值缺少校验
你调用的GraphQL商品查询是异步接口,首次渲染时接口还未返回数据,setProducts拿到的nodes是带空值的占位数组,数组内的商品项还未填充slug字段;另外渲染商品价格、图片时你给SimpleProduct字段加了可选链?.做兜底,但是点击事件取product.slug时没有做任何非空判断,直接把可能为undefined的slug传入跳转方法,最终拼接出来的路径最后一段就会出现undefined,也就是你遇到的/shop/shoes/undefined问题。
修复方式
- 补全
useCallback依赖数组,把categorySlug加入依赖项; - 增加加载态判断,商品数据未返回完成时,不渲染可点击的商品卡片;
- 跳转逻辑增加参数校验,传入的productSlug为空时直接return不执行路由跳转;
- 商品字段取值统一加可选链兜底,比如取slug时写
product?.slug,避免直接读取undefined属性。
内容的提问来源于stack exchange,提问作者user8566930
相关产品推荐
相关产品推荐

