Next.js中如何通过FormData实现表单文本字段行分组?
问题描述
在Next.js中开发表单时,希望提交的FormData能按预期分组,但当前实现不符合需求。表单代码如下:
<form action={formAction} className="flex flex-col w-full justify-around"> <div className="relative"> <Options data={activeUsers.users} name="user" id="user" display1="firstName" display2="lastName" label="Employee" htmlFor="user" hideLabel={false} /> <table className="mt-5"> <thead> <tr> <th>Success Indicator</th> <th>MOV</th> <th>Aspect Of Work</th> </tr> </thead> <tbody> {inputRows.map((item: any, i: number) => ( <tr key={i}> <td> <input type="text" id="mov" name={`test[${i}]`} placeholder="MOV" className="rounded-lg py-1 px-1 m-2 text-black" required /> </td> <td> <input type="text" id="aspects_of_work" name={`test[${i}]`} placeholder="Aspect of work" className="rounded-lg py-1 px-1 m-2 text-black" required /> </td> </tr> ))} </tbody> </table> <SubmitBtn /> </div> <p role="status">{state?.message}</p> </form>
预期与实际结果
预期的FormData结构:
{ "user": 1, "test": [ { "mov": "value here", "aspects_of_work": "value here" }, { "mov": "value here", "aspects_of_work": "value here" } ] }
实际得到的FormData条目:
[ { name: 'user', value: '1' }, { name: 'test[0]', value: '5' }, { name: 'test[0]', value: 'qwe' }, { name: 'test[1]', value: '27' }, { name: 'test[1]', value: 'rrr' }, { name: 'test[1]', value: 'rrr' } ]
误区:误以为input的id会成为FormData的属性键,实际上FormData只读取name属性的值作为键。
解决方案
1. 修改input的name属性格式
要让FormData的键支持嵌套结构,需要调整name的命名规则,给每个字段加上对应的属性名:
{inputRows.map((item: any, i: number) => ( <tr key={i}> <td> <input type="text" id={`mov-${i}`} name={`test[${i}][mov]`} placeholder="MOV" className="rounded-lg py-1 px-1 m-2 text-black" required /> </td> <td> <input type="text" id={`aspects_of_work-${i}`} name={`test[${i}][aspects_of_work]`} placeholder="Aspect of work" className="rounded-lg py-1 px-1 m-2 text-black" required /> </td> </tr> ))}
注意:这里把id改成带索引的唯一值,避免DOM中出现重复id(符合HTML规范要求)。
2. 解析FormData为预期结构
原生FormData是扁平的键值对集合,不会自动转换成嵌套对象数组,需要手动解析或借助工具库:
方式一:手动解析(Server Action示例)
async function formAction(formData: FormData) { const user = formData.get('user') as string; const test = []; let index = 0; // 循环读取每个索引对应的字段 while (formData.has(`test[${index}][mov]`)) { test.push({ mov: formData.get(`test[${index}][mov]`) as string, aspects_of_work: formData.get(`test[${index}][aspects_of_work]`) as string }); index++; } // 此时得到的结构就符合预期 console.log({ user, test }); }
方式二:用工具库简化解析(如zod-form-data)
借助zod-form-data可以快速将FormData解析为结构化数据:
import { z } from 'zod'; import { zfd } from 'zod-form-data'; // 定义表单结构的校验规则 const FormSchema = zfd.formData({ user: zfd.text(), test: z.array( z.object({ mov: zfd.text(), aspects_of_work: zfd.text() }) ) }); async function formAction(formData: FormData) { // 解析并校验FormData const parsedData = FormSchema.parse(formData); console.log(parsedData); // 输出预期的嵌套结构 }
关于Rails与Next.js的差异
Rails框架默认支持解析test[0][mov]这类命名格式,自动转换成嵌套对象数组;但Next.js没有内置该功能,需要开发者手动处理或使用工具库实现。
内容的提问来源于stack exchange,提问作者aRtoo
相关产品推荐
相关产品推荐

