错误序列化从`getStaticProps`返回的`.product` | 使用NextJS和Stripe

huangapple go评论87阅读模式
英文:

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 &quot;next/image&quot;;
import { stripe } from &quot;src/utils/stripe&quot;;
import { CheckIcon, MinusSmallIcon, PlusSmallIcon } from &quot;@heroicons/react/24/solid&quot;
import { formatCurrencyString, useShoppingCart } from &quot;use-shopping-cart&quot;;
import { useState } from &quot;react&quot;;
import { toast } from &quot;react-hot-toast&quot;;
export default function ProductPage({ product }) {
const [count, setCount] =  useState(1);
const { addItem } = useShoppingCart()
function onAddToCart(event) {
event.preventDefault();
const id = toast.loading(&quot;Adding 1 item...&quot;);
addItem(product);
toast.success(product.name, { id });
}
return (
&lt;div className=&quot;container lg:max-w-screen-lg mx-auto py-12 px-6&quot;&gt;
&lt;div className=&quot;flex flex-col md:flex-row justify-between items-center space-y-8 md:space-y-0 md:space-x-12&quot;&gt;
&lt;div className=&quot;relatie w-72 h-72 sm:w-96 sm:h-96&quot;&gt;
&lt;Image src={product.image} alt={product.name} fill style={{objectFit: &quot;contain&quot; }} sizes=&quot;100%&quot; priority /&gt;
&lt;/div&gt;
&lt;div className=&quot;w-full flex-1 max-w-md border border-opacity-50 rounded-md shadow-lg p-6 bg-whitie&quot;&gt;
&lt;h2 className=&quot;text-3x1 font-semibold&quot;&gt;{product.name}&lt;/h2&gt;
&lt;p className=&quot;pt-2 flex items-center space-x-2&quot;&gt;
&lt;CheckIcon className=&quot;text-lime-500 w-5 h-5&quot; /&gt;
&lt;span className=&quot;font-semibold&quot;&gt;In stock&lt;/span&gt;
&lt;/p&gt;
&lt;div className=&quot;mt-4 border-t pt-4&quot;&gt;
&lt;p className=&quot;text-gray-500&quot;&gt;Price: &lt;/p&gt;
&lt;p className=&quot;text-xl front-semibold&quot;&gt;{formatCurrencyString({ value: product.price, currency: product.currency,})} &lt;/p&gt;
&lt;/div&gt;
&lt;div className=&quot;mt-4 border-t pt-4&quot;&gt;
&lt;p className=&quot;text-gray-500&quot;&gt;Quantity&lt;/p&gt;
&lt;div className=&quot;mt-1 flex items-center space-x-3&quot;&gt;
&lt;button disabled={count &lt;= 1} onClick={() =&gt; setCount(count - 1)} className=&quot;p-1 rounded-md hover:bg-rose-100 hover:text-rose-500&quot;&gt;
&lt;MinusSmallIcon className=&quot;w-6 h-6 flex-shrink-0&quot;/&gt;
&lt;/button&gt;
&lt;p className=&quot;font-semibold text-xl&quot;&gt;{count}&lt;/p&gt;
&lt;button onAbort={() =&gt; setCount(count + 1)} className=&quot;p-1 rounded-md hover:bg-green-100 hover:text-green-500&quot;&gt;
&lt;PlusSmallIcon className=&quot;w-6 h-6 flex-shrink-0&quot;/&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;button className=&quot;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&quot;&gt;
Add to cart
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
);
}
export async function getStaticPaths() {
const inventory = await stripe.products.list();
const paths = inventory.data.map((product) =&gt; ({
params: { id: product.id },
}));
return {
paths, 
fallback: &quot;blocking&quot;
};
}
export async function getStaticProps({ params }) {
const inventory = await stripe.products.list({
expand: [&quot;data.default_price&quot;],
});
const products = inventory.data.map((product) =&gt; {
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) =&gt; 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 中有未定义的值。

根据您的需求,有两种处理方式:

  1. 可能是最符合 NextJS 的方式,如果未找到产品,则返回 notFound: true 而不是 props,这将导致显示 404 页面:
if (!product) {
    return {
        notFound: true
    };
}
  1. 如果未找到产品,则返回除 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:

  1. 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
};
}
  1. 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 &amp;&amp; { 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) =&gt; {
+ const products = inventory?.data?.map((product) =&gt; {
英文:

Sounds like products might be nullish. Optional chaining could help here:

- const products = inventory.data.map((product) =&gt; {
+ const products = inventory?.data?.map((product) =&gt; {

huangapple
  • 本文由 发表于 2023年7月28日 02:01:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76782351.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定