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

在Next.js 14中使用useFormState为服务器动作传递多参数

使用useFormState传递多参数给服务器动作的解决方案

你的服务器动作参数顺序已经符合useFormState要求(第一个参数为prevState),但默认表单提交仅能传递FormData,额外的tagsArr需要通过包装服务器动作的方式传递,具体实现如下:

步骤1:客户端组件中包装服务器动作

在客户端组件内,导入useFormState和目标服务器动作,同时用useState管理tagsArr状态,通过包装函数将tagsArr作为额外参数传入原始服务器动作:

"use client";

import { useFormState } from "react-dom";
import { useState } from "react";
import { addBlogPost } from "@/actions/addBlogPost"; // 替换为你的服务器动作实际路径
import type { TagType } from "@/actions/addBlogPost";

export default function BlogPostForm() {
  // 管理用户选择的标签数组
  const [tagsArr, setTagsArr] = useState<TagType[]>([]);
  // 初始化表单反馈状态
  const initialState = { status: 200, message: "" };

  // 包装服务器动作,注入tagsArr参数
  const [feedbackState, formAction] = useFormState(
    async (prevState, formData) => {
      return addBlogPost(prevState, formData, tagsArr);
    },
    initialState
  );

  // 示例:标签选择逻辑
  const handleTagSelect = (tag: TagType) => {
    setTagsArr(prev => [...prev, tag]);
  };

  return (
    <form action={formAction}>
      {/* 表单核心输入项 */}
      <input type="text" name="title" placeholder="文章标题" required />
      <textarea name="content" placeholder="文章内容" required></textarea>
      <input type="hidden" name="userId" value="当前用户ID" />
      
      {/* 标签选择区域示例 */}
      <button type="button" onClick={() => handleTagSelect({ id: "1", name: "前端开发" })}>
        选择「前端开发」标签
      </button>

      {/* 提交按钮 */}
      <button type="submit">发布文章</button>

      {/* 显示表单反馈 */}
      {feedbackState.message && (
        <div className={`feedback-${feedbackState.status === 400 ? "error" : "success"}`}>
          {feedbackState.message}
        </div>
      )}
    </form>
  );
}

步骤2:优化服务器动作的类型与逻辑

将prevState的any类型替换为具体类型,同时简化数据库关联逻辑(connect仅需标签ID即可):

"use server";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();

type TagType = {
  id: string;
  name: string;
};

// 定义表单反馈状态的具体类型
type FormFeedbackState = {
  status: number;
  message: string;
};

export const addBlogPost = async (
  prevState: FormFeedbackState,
  formData: FormData,
  tagsArr: TagType[]
) => {
  try {
    const title = formData.get("title") as string;
    const content = formData.get("content") as string;
    const userId = formData.get("userId") as string;

    if (!title.trim() || !content.trim()) {
      return {
        status: 400,
        message: "必须填写标题和内容",
      };
    }

    const blogPost = await prisma.blogPost.create({
      data: {
        title,
        content,
        user: { connect: { id: userId } },
        tags: {
          connect: tagsArr.map((tag) => ({ id: tag.id })),
          // 注意:关联已存在的标签仅需传递ID,无需name
        },
      },
    });

    return {
      status: 200,
      message: "文章发布成功",
    };
  } catch (error) {
    console.error(error);
    return {
      status: 500,
      message: "服务器错误,请稍后重试",
    };
  }
};

关键说明

  • useFormState绑定的动作默认仅接收prevState和formData两个参数,因此需要通过包装函数将额外的tagsArr注入到原始服务器动作的调用中。
  • 确保tagsArr是可序列化的(此处TagType仅包含字符串,符合要求),否则服务器端无法正确解析。
  • 数据库关联标签时,connect只需传递标签ID即可,无需重复传递name(若标签已存在于数据库)。

内容的提问来源于stack exchange,提问作者Robikk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 14:46:20