如何使用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;
关键说明
- 字段路径的正确性:嵌套的
useFieldArray的name必须是variety[${varietyIndex}].price,这样React Hook Form才能精准定位到当前variety项内的price数组。 - 增删逻辑的关联:
- 添加
variety时,必须在appendVariety的对象中包含初始化的price数组(你之前的这部分代码是正确的); - 删除
variety时,removeVariety会自动移除对应的price数组,无需额外处理。
- 添加
- 输入框的注册:每个表单控件的
register路径要严格对应层级,比如variety[${varietyIndex}].price[${priceIndex}].sizeId,确保数据能正确绑定到表单状态中。
内容的提问来源于stack exchange,提问作者Riklia
相关产品推荐
相关产品推荐

