您好,我正在努力找出如何在 Nextjs 13 中使用服务器操作成功进行数据突变后调用 toast。toast 是一个客户端组件,因为它使用钩子/上下文。
我该怎么做?
简短的回答,没有规范的方法可以做到这一点。 有解决方法。尽管它们可能相关,但根据您渲染的是服务器还是客户端组件,它们可能会有所不同。
老实说,next 没有提供开箱即用的解决方案,让用户想出他们能想到的任何解决方案,包括可能的糟糕解决方案,这很糟糕。
参见 cookie 示例:
// Server action file
"use server";
import { cookies } from "next/headers";
...
// At the end of your server action function:
cookies()
.set(
"my-app-toast-msg",
JSON.stringify({error: true, msg: "wrong credentials"}, {
expires: new Date(Date.now() + 10 * 1000), // 10 seconds
})
);
// At your server component file
import { cookies } from "next/headers";
export async function Page() {
const toastMsg = cookies().get("my-app-toast-msg");
return (
<>
{!!toastMsg &&
JSON.parse(toastMsg).error &&
<ClientErrorToast errorMsg={JSON.parse(toastMsg).msg} />
...
</>
)
}
这是一个快速的、肮脏的、未经测试的内存实现,我不知道这是否是一个好的解决方案。 我使用一个错误作为例子,但你应该能够看到这个想法。
我能够使用
useFormStatus()
钩子实现此结果。
假设您有一个使用服务器操作的表单组件。这是一个基本的服务器组件,可能会调用您的服务器操作,在另一个文件中定义:
import { submit } from "./serverAction";
export function ServerForm() {
return (
<form action={submit}>
<input type="email" name="email" />
<input type="text" name="name" />
<input type="submit" />
</form>
);
}
要管理服务器操作返回的数据,例如,显示每个输入附近的错误,您需要将此表单转换为客户端组件:
"use client"
import { submit } from "./serverAction";
// @ts-ignore: Experimental feature not yet in react-dom type definitions
import { experimental_useFormState as useFormState } from "react-dom";
export function ClientForm() {
// You pass your ServerAction and an initial state to `useFormState`
const [state, formAction] = useFormState(submit, {
error: { name: null, email: null },
message: null,
});
return (
<form action={formAction}>
<input type="email" name="email" />
<p>{state?.error?.email}</p>
<input type="text" name="name" />
<p>{state?.error?.name}</p>
<input type="submit" />
</form>
);
}
在这种情况下,我从 ServerAction 返回一个对象:
"use server";
export async function submit(formData: FormData) {
// Extract data from the submitted form
const name = formData.get("name") as string;
const email = formData.get("email") as string;
try {
// You might do data validation and database interactions here
// On success, you'd probably redirect instead of returning an object
return { error: null, message: `New submission: ${name} - ${email}` };
} catch (e) {
// Error handling
return {
error: {
name: "There was an error with this name",
email: "There was an error with this email",
},
message: "Failed submission",
};
}
}
您可以通过设置观察
useEffect
的 state
来无缝触发烤面包机。例如,使用 shadcn 的 toaster:
"use client";
import { useToast } from "@components/shadcn-ui-registry/use-toast";
import { useEffect } from "react";
import { submit } from "./serverAction";
// @ts-ignore: Experimental feature not yet in react-dom type definitions
import { experimental_useFormState as useFormState } from "react-dom";
export function ClientForm() {
// You pass your ServerAction and an initial state to `useFormState`
const [state, formAction] = useFormState(submit, {
error: { name: null, email: null },
message: null,
});
const { toast } = useToast();
useEffect(() => {
toast({
title: state?.error?.name || state?.error?.email ? "Error" : "Success",
description: state.message,
});
}, [state, toast]);
return (
<form action={formAction}>
<input type="email" name="email" />
<p>{state?.error?.email}</p>
<input type="text" name="name" />
<p>{state?.error?.name}</p>
<input type="submit" />
</form>
);
}
有关更多详细信息,请参阅Next.js 表单和突变文档。希望这对您有所帮助,请随时提供反馈或询问任何不清楚的地方。