在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
相关产品推荐
相关产品推荐

