英文:
Error serializing `.product` returned from `getStaticProps` | using NextJS and Stripe
问题
我遇到了以下错误:"错误:从“/products/[id]”中的getStaticProps返回的.product无法序列化。原因:未定义的值无法序列化为JSON。请使用null或省略此值。"
这发生在我尝试为我的电子商务网站创建单个产品页面时。
我的代码在"/products/[id]"中:
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="relative 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-white">
<h2 className="text-3xl 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 font-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,
};
}
我已经查看了一些解决方案,建议使用JSON.parse(JSON.stringify("string"))
,但没有成功。
我还从黑匣子中得到了这个反馈,但该函数已经是异步的,当调用getStaticProps
函数时,product
变量仍然未定义。这是因为stripe.products.list()
函数是异步的,而getStaticProps
函数是同步的。
英文:
I encountered the following error "Error: Error serializing .product returned from getStaticProps in "/products/[id]".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value."
This happened when I tried creating individual product pages for my e-commerce website.
My code in "/products/[id]"
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,
};
}
I have looked at a few solutions suggesting to JSON.parse(JSON.stringify("string")). That did not work.
I Also got this back from blackbox but the function is already asynchronous, The product
variable is still undefined when the getStaticProps
function is called. This is because the stripe.products.list()
function is asynchronous, and the getStaticProps
function is synchronous.
答案1
得分: 1
可能发生的情况是您的 products.find
实际上未能匹配到任何产品与您的 params.id。这导致 product
未定义,然后 NextJS 不允许在 props 中有未定义的值。
根据您的需求,有两种处理方式:
- 可能是最符合 NextJS 的方式,如果未找到产品,则返回
notFound: true
而不是 props,这将导致显示 404 页面:
if (!product) {
return {
notFound: true
};
}
- 如果未找到产品,则返回除 undefined 以外的其他值:
return {
props: {
product: product ?? null,
},
revalidate: 60 * 60,
};
或者,除非定义了 product
,否则不添加 product prop:
return {
props: {
...(product && { product }),
},
revalidate: 60 * 60,
};
只有在您的客户端设置了处理 product
缺失的情况时才使用第二种方式。
英文:
What's likely happening is that your products.find
is not actually matching any products to your params.id. That results in product
being undefined, and then NextJS doesn't like you having an undefined value in props.
A couple ways around this, depending on what you need:
- Perhaps the most NextJS way of handing this, if product is not found, then return
notFound: true
instead of props, resulting in the 404 page
if (!product) {
return {
notFound: true
};
}
- Return a value other than undefined if product is not found:
return {
props: {
product: product ?? null,
},
revalidate: 60 * 60,
};
or, don't add the product prop at all unless it's defined
return {
props: {
...(product && { product }),
},
revalidate: 60 * 60,
};
Only use #2 if your client is set up to handle the case where product
is missing.
答案2
得分: 0
听起来products
可能是空值。可选链在这里可能有所帮助:
- const products = inventory.data.map((product) => {
+ const products = inventory?.data?.map((product) => {
英文:
Sounds like products
might be nullish. Optional chaining could help here:
- const products = inventory.data.map((product) => {
+ const products = inventory?.data?.map((product) => {
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论