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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:50:26