NextJS+Stripe:getStaticProps返回product序列化错误求助
Next.js单个商品页面序列化错误解决办法
Error: Error serializing .product returned from getStaticProps in "/products/[id]". Reason: undefined cannot be serialized as JSON. Please use null or omit this value.
问题代码
import Image from "next/image"; import { stripe } from "src/utils/stripe"; import { CheckIcon, MinusSmallIcon, PlusSmallIcon } from "@heroicons/react/24/solid" import { formatCurrencyString, useShoppingCart } from "use-shopping-cart"; import { useState } from "react"; import { toast } from "react-hot-toast"; export default function ProductPage({ product }) { const [count, setCount] = useState(1); const { addItem } = useShoppingCart() function onAddToCart(event) { event.preventDefault(); const id = toast.loading("Adding 1 item..."); addItem(product); toast.success(product.name, { id }); } return ( <div className="container lg:max-w-screen-lg mx-auto py-12 px-6"> <div className="flex flex-col md:flex-row justify-between items-center space-y-8 md:space-y-0 md:space-x-12"> <div className="relatie w-72 h-72 sm:w-96 sm:h-96"> <Image src={product.image} alt={product.name} fill style={{objectFit: "contain" }} sizes="100%" priority /> </div> <div className="w-full flex-1 max-w-md border border-opacity-50 rounded-md shadow-lg p-6 bg-whitie"> <h2 className="text-3x1 font-semibold">{product.name}</h2> <p className="pt-2 flex items-center space-x-2"> <CheckIcon className="text-lime-500 w-5 h-5" /> <span className="font-semibold">In stock</span> </p> <div className="mt-4 border-t pt-4"> <p className="text-gray-500">Price: </p> <p className="text-xl front-semibold">{formatCurrencyString({ value: product.price, currency: product.currency,})} </p> </div> <div className="mt-4 border-t pt-4"> <p className="text-gray-500">Quantity</p> <div className="mt-1 flex items-center space-x-3"> <button disabled={count <= 1} onClick={() => setCount(count - 1)} className="p-1 rounded-md hover:bg-rose-100 hover:text-rose-500"> <MinusSmallIcon className="w-6 h-6 flex-shrink-0"/> </button> <p className="font-semibold text-xl">{count}</p> <button onAbort={() => setCount(count + 1)} className="p-1 rounded-md hover:bg-green-100 hover:text-green-500"> <PlusSmallIcon className="w-6 h-6 flex-shrink-0"/> </button> </div> </div> <button className="w-full mt-4 border border-lime-500 py-2 px-2 bg-lime-500 hover:bg-lime-600 hover:border-lime-600 focus:ring-4 focus:ring-opacity-50 focus:ring-lime-500 text-white disabled:opacity-50 disabled:cursor-not-allowed rounded-md"> Add to cart </button> </div> </div> </div> ); } export async function getStaticPaths() { const inventory = await stripe.products.list(); const paths = inventory.data.map((product) => ({ params: { id: product.id }, })); return { paths, fallback: "blocking" }; } export async function getStaticProps({ params }) { const inventory = await stripe.products.list({ expand: ["data.default_price"], }); const products = inventory.data.map((product) => { const price = product.default_price; return { currency: price.currency, id: product.id, name: product.name, price: price.unit_amount, image: product.images[0], }; }); const product = products.find((product) => product.id === params.id); return { props: { product, }, revalidate: 60 * 60, }; }
核心原因
- product变量可能为undefined:当
products.find未匹配到对应ID的商品时,返回undefined,而Next.js不允许undefined作为序列化的props返回。 - Stripe API调用冗余:每次拉取所有商品再筛选,效率低且容易出现匹配失败的情况。
- 代码存在语法/拼写错误:比如按钮事件绑定错误、样式类名拼写错误,虽不直接触发序列化错误,但会影响页面功能。
解决步骤
1. 重构getStaticProps,确保product有效
直接通过商品ID查询单个商品,同时处理查询失败的情况:
export async function getStaticProps({ params }) { try { // 单独查询目标商品,展开默认价格 const product = await stripe.products.retrieve(params.id, { expand: ["default_price"], }); if (!product) { return { notFound: true }; // 返回404页面 } const price = product.default_price; const formattedProduct = { currency: price.currency, id: product.id, name: product.name, price: price.unit_amount, image: product.images[0] || "", // 兜底空字符串,避免undefined }; return { props: { product: formattedProduct }, revalidate: 60 * 60, }; } catch (error) { // 捕获API错误,返回404 return { notFound: true }; } }
2. 增强组件健壮性
在组件开头添加商品存在性判断:
export default function ProductPage({ product }) { if (!product) { return <div className="container mx-auto py-12 text-center">商品不存在或已下架</div>; } // 剩余组件代码... }
3. 修复代码中的其他错误
- 将加号按钮的
onAbort改为onClick - 修正样式类名:
relatie→relativebg-whitie→bg-whitetext-3x1→text-3xlfront-semibold→font-semibold
4. 优化getStaticPaths(可选)
添加错误处理,避免API调用失败导致路径生成异常:
export async function getStaticPaths() { try { const inventory = await stripe.products.list(); const paths = inventory.data.map((product) => ({ params: { id: product.id }, })); return { paths, fallback: "blocking" }; } catch (error) { return { paths: [], fallback: "blocking" }; } }
内容的提问来源于stack exchange,提问作者2G-O
相关产品推荐
相关产品推荐

