mcpField
S
safe-action-forms

safe-action-forms

github.com/next-safe-action开发11.2k 次安装8 周前更新
代码仓库

关于 safe-action-forms

介绍 next-safe-action 表单集成方式的技能,涵盖原生表单和 React Hook Form 适配器。帮助开发者根据表单状态、验证需求及乐观更新选择相应 Hook。

主要功能

  1. 01使用 useAction 配合原生表单
  2. 02用 useStateAction 跟踪表单状态
  3. 03接入 React Hook Form 适配器
  4. 04支持表单乐观更新
  5. 05处理 FormData 与架构要求

适用场景

  1. 01构建简单的 Action 表单
  2. 02显示字段级验证错误
  3. 03创建支持乐观更新的表单

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/next-safe-action/skills --skill safe-action-forms

安装后在 Claude Code 中输入/safe-action-forms确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

file-uploads.md
form-actions.md
react-hook-form.md

详细介绍

next-safe-action 表单集成

选项

方案适用场景
useAction + 原生表单简单表单、无需复杂的校验 UI、程序化触发
useStateAction + <form action={formAction}>需要跟踪状态、访问 prevResult 和使用完整回调的表单
useHookFormAction(RHF 适配器)需要字段级错误、在更改或失焦时进行校验的复杂表单
useHookFormOptimisticAction需要乐观 UI 更新的 RHF 表单
useOptimisticStateAction需要立即更新 UI,且必须将重叠写入排队并累积的场景(参见 hooks 技能文档)

快速开始 — useStateAction 表单

"use client";

import { useStateAction } from "next-safe-action/hooks";
import { submitContact } from "@/app/actions";

export function ContactForm() {
  const { formAction, result, isPending, hasSucceeded } = useStateAction(submitContact, {
    onSuccess: () => toast.success("Message sent!"),
  });

  return (
    <form action={formAction}>
      <input name="name" required />
      <input name="email" type="email" required />
      <textarea name="message" required />

      {result.validationErrors?.email && (
        <p>{result.validationErrors.email._errors?.[0]}</p>
      )}
      {result.serverError && <p>{result.serverError}</p>}
      {hasSucceeded && <p>Message sent!</p>}

      <button type="submit" disabled={isPending}>
        {isPending ? "Sending..." : "Send"}
      </button>
    </form>
  );
}

注意:useStateAction 要求服务器操作使用 .stateAction() 而不是 .action() 定义;<form action={formAction}> 会传入原始 FormData,因此输入架构必须能够解析 FormData(例如,使用 zod-form-data 中的 zfd.formData({...}))。有关何时使用 useAction 与 useStateAction 的完整决策表,请参见 hooks 技能文档。

快速开始 — 原生表单

"use client";

import { useAction } from "next-safe-action/hooks";
import { submitContact } from "@/app/actions";

export function ContactForm() {
  const { execute, result, isPending } = useAction(submitContact);

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        const fd = new FormData(e.currentTarget);
        execute({
          name: fd.get("name") as string,
          email: fd.get("email") as string,
          message: fd.get("message") as string,
        });
      }}
    >
      <input name="name" required />
      <input name="email" type="email" required />
      <textarea name="message" required />

      {result.validationErrors && (
        <p>{result.validationErrors.email?._errors?.[0]}</p>
      )}
      {result.serverError && <p>{result.serverError}</p>}
      {result.data && <p>Message sent!</p>}

      <button type="submit" disabled={isPending}>
        {isPending ? "Sending..." : "Send"}
      </button>
    </form>
  );
}

快速开始 — React Hook Form 适配器

"use client";

import { useHookFormAction } from "@next-safe-action/adapter-react-hook-form/hooks";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { submitContact } from "@/app/actions";

const schema = z.object({
  name: z.string().min(1, "Name is required"),
  email: z.string().email("Invalid email"),
  message: z.string().min(10, "Message must be at least 10 characters"),
});

export function ContactForm() {
  const { form, handleSubmitWithAction, action } = useHookFormAction(
    submitContact,
    zodResolver(schema),
    {
      actionProps: {
        onSuccess: () => toast.success("Message sent!"),
      },
    }
  );

  return (
    <form onSubmit={handleSubmitWithAction}>
      <input {...form.register("name")} />
      {form.formState.errors.name && <p>{form.formState.errors.name.message}</p>}

      <input {...form.register("email")} />
      {form.formState.errors.email && <p>{form.formState.errors.email.message}</p>}

      <textarea {...form.register("message")} />
      {form.formState.errors.message && <p>{form.formState.errors.message.message}</p>}

      {action.result.serverError && <p>{action.result.serverError}</p>}

      <button type="submit" disabled={action.isPending}>
        {action.isPending ? "Sending..." : "Send"}
      </button>
    </form>
  );
}

相关文档

入口点

包入口点导出项
@next-safe-action/adapter-react-hook-form默认mapToHookFormErrors、类型
@next-safe-action/adapter-react-hook-form/hooksHooksuseHookFormAction、useHookFormOptimisticAction、useHookFormActionErrorMapper

评论

支持 Markdown