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

如何使用react-hook-form动态增删嵌套数组中的对象?

解决React Hook Form中嵌套动态数组(variety内的price数组)的管理问题

要处理嵌套的动态数组,核心是在每个variety项内部再使用一次useFieldArray钩子,通过正确的字段路径关联到对应层级的数组。以下是完整的可实现方案:

步骤1:初始化表单与顶层useFieldArray

首先用useForm初始化表单,设置符合结构的默认值,再用useFieldArray管理顶层的variety数组:

import { useForm, useFieldArray } from 'react-hook-form';

function ProductForm() {
  const { register, control, handleSubmit, formState: { errors } } = useForm({
    defaultValues: {
      variety: [
        {
          colorId: "",
          image: [],
          price: [
            { sizeId: "", price: 0, amount: 0 }
          ]
        }
      ]
    }
  });

  // 管理顶层的variety数组
  const { fields: varietyFields, append: appendVariety, remove: removeVariety } = useFieldArray({
    control,
    name: "variety"
  });

步骤2:在每个variety项内嵌套使用useFieldArray处理price数组

遍历varietyFields时,对每个项单独初始化useFieldArray,字段路径要指定为variety[${index}].price,确保关联到当前variety项的price数组:

const onSubmit = (data) => {
    console.log("表单数据:", data);
    // 这里可以处理表单提交逻辑
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* 遍历每个variety项 */}
      {varietyFields.map((variety, varietyIndex) => {
        // 管理当前variety项内的price数组
        const { fields: priceFields, append: appendPrice, remove: removePrice } = useFieldArray({
          control,
          name: `variety[${varietyIndex}].price`
        });

        return (
          <div key={variety.id} style={{ border: "1px solid #eee", padding: "1rem", marginBottom: "1rem" }}>
            {/* Color ID输入框 */}
            <div>
              <label>Color ID</label>
              <input
                {...register(`variety[${varietyIndex}].colorId`, { required: true })}
                placeholder="输入颜色ID"
              />
              {errors?.variety?.[varietyIndex]?.colorId && <span>颜色ID为必填项</span>}
            </div>

            {/* 动态price数组区域 */}
            <div style={{ margin: "1rem 0" }}>
              <h4>价格规格</h4>
              {/* 遍历每个price项 */}
              {priceFields.map((price, priceIndex) => (
                <div key={price.id} style={{ display: "flex", gap: "1rem", marginBottom: "0.5rem" }}>
                  <div>
                    <label>Size ID</label>
                    <input
                      {...register(`variety[${varietyIndex}].price[${priceIndex}].sizeId`, { required: true })}
                      placeholder="输入尺寸ID"
                    />
                  </div>
                  <div>
                    <label>价格</label>
                    <input
                      type="number"
                      {...register(`variety[${varietyIndex}].price[${priceIndex}].price`, { required: true, min: 0 })}
                      placeholder="输入价格"
                    />
                  </div>
                  <div>
                    <label>库存</label>
                    <input
                      type="number"
                      {...register(`variety[${varietyIndex}].price[${priceIndex}].amount`, { required: true, min: 0 })}
                      placeholder="输入库存"
                    />
                  </div>
                  {/* 删除当前price项 */}
                  <button
                    type="button"
                    onClick={() => removePrice(priceIndex)}
                    disabled={priceFields.length === 1} // 至少保留一个price项
                  >
                    删除规格
                  </button>
                </div>
              ))}
              {/* 添加新的price项 */}
              <button type="button" onClick={() => appendPrice({ sizeId: "", price: 0, amount: 0 })}>
                添加规格
              </button>
            </div>

            {/* 删除当前variety项 */}
            <button
              type="button"
              onClick={() => removeVariety(varietyIndex)}
              disabled={varietyFields.length === 1} // 至少保留一个variety项
            >
              删除颜色规格
            </button>
          </div>
        );
      })}

      {/* 添加新的variety项 */}
      <button
        type="button"
        onClick={() => appendVariety({
          colorId: "",
          image: [],
          price: [{ sizeId: "", price: 0, amount: 0 }]
        })}
      >
        添加颜色规格
      </button>

      {/* 表单提交按钮 */}
      <button type="submit" style={{ marginTop: "1rem" }}>
        提交表单
      </button>
    </form>
  );
}

export default ProductForm;

关键说明

  1. 字段路径的正确性:嵌套的useFieldArray的name必须是variety[${varietyIndex}].price,这样React Hook Form才能精准定位到当前variety项内的price数组。
  2. 增删逻辑的关联:
    • 添加variety时,必须在appendVariety的对象中包含初始化的price数组(你之前的这部分代码是正确的);
    • 删除variety时,removeVariety会自动移除对应的price数组,无需额外处理。
  3. 输入框的注册:每个表单控件的register路径要严格对应层级,比如variety[${varietyIndex}].price[${priceIndex}].sizeId,确保数据能正确绑定到表单状态中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 17:04:55