Next.js 14中Server Actions结合react-hot-toast的实现方案
解决Next.js 14中Server Actions结合react-hot-toast的问题
1. 优化Server Action逻辑
首先修正updateUser的判断逻辑,数据库UPDATE操作通常返回受影响行数,而非直接返回用户对象,以此判断更新是否成功:
// app/lib/actions.js "use server"; import { revalidatePath } from "next/cache"; export const updateUser = async (prevState, formData) => { const { id, name, email, number, role, status } = Object.fromEntries(formData); try { const result = await query({ query: "UPDATE users SET name = ?, email = ?, number = ?, role = ?, status = ? WHERE id = ?", values: [name, email, number, role, status, id], }); // 根据数据库驱动返回的受影响行数判断(以mysql2为例) if (result.affectedRows === 0) { return { message: "User Update Failed", type: "error" }; } revalidatePath("/dashboard/grit/edit"); return { message: "User Update Successfully", type: "success" }; } catch (error) { console.error("Update error:", error); return { message: "User Update Failed", type: "error" }; } };
2. 调整客户端组件逻辑
移除按钮点击直接触发toast的逻辑,改用useEffect监听useFormState返回的state,根据Server Action执行结果触发对应提示:
"use client"; import { useFormState, useFormStatus } from "react-dom"; import { MdModeEdit } from "react-icons/md"; import { updateUser } from "@/app/lib/actions"; import toast from "react-hot-toast"; import { useEffect } from "react"; const UpdateUser = ({ id, name, email, number, role, status }) => { const [state, formAction] = useFormState(updateUser, null); // 监听state变化,触发对应toast提示 useEffect(() => { if (!state) return; if (state.type === "success") { toast.success(state.message, { style: { background: "#228B22", color: "#fff", }, }); document.getElementById(id)?.close(); } else if (state.type === "error") { toast.error(state.message, { style: { background: "#DC143C", color: "#fff", }, }); } }, [state, id]); function Submit() { const { pending } = useFormStatus(); return ( <button type="submit" className="btn btn-sm btn-neutral text-white h-[6vh] w-[35vh] rounded-md " disabled={pending} > {pending ? "Updating..." : "Update User"} </button> ); } return ( <form action={formAction}> <input type="hidden" name="id" value={id} /> <input type="text" name="name" defaultValue={name} required /> <input type="email" name="email" defaultValue={email} required /> <input type="text" name="number" defaultValue={number} /> <select name="role" defaultValue={role} required> {/* 补充你的角色选项 */} </select> <select name="status" defaultValue={status} required> {/* 补充你的状态选项 */} </select> <Submit /> </form> ); }; export default UpdateUser;
3. 全局注册Toaster组件
在根布局文件(如app/layout.js)中挂载Toaster组件,确保toast能正常渲染:
// app/layout.js import { Toaster } from "react-hot-toast"; export default function RootLayout({ children }) { return ( <html lang="en"> <body> {children} <Toaster position="top-right" /> </body> </html> ); }
核心说明
- 不再提前触发toast,等待Server Action执行完成后,通过state结果判断提示类型,逻辑更准确
- Server Action返回结构化结果,方便客户端区分成功/失败状态
- 利用
useFormStatus处理按钮加载状态,提升用户体验 - 全局挂载
Toaster是toast正常显示的前提
内容的提问来源于stack exchange,提问作者Siratul Islam
相关产品推荐
相关产品推荐

