You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 08:14:57