如何在react-hook-form中更新嵌套数组结构的表单数据?
React Hook Form嵌套数组表单数据无法正确传入数据库问题排查
最近在使用react-hook-form处理嵌套数组结构的表单数据更新时,遇到了数据无法正确传入数据库的问题——无报错但提交数据异常。目标是通过useFieldArray钩子实现嵌套数组的表单数据更新,以下是相关代码及排查修复方案:
字段结构定义
export const defects = { title: 'Defects', fields: [ { title: '缺陷位置:', name: 'defects.location', type: 'checkbox', fields: [ { title: '室内', type: 'checkbox', name: 'defects.location.interior', }, { title: '屋顶空间', type: 'checkbox', name: 'defects.location.roofSpace', }, { title: '其他', type: 'textarea', name: 'defects.location.other', }, ], }, { title: '缺陷摘要:', name: 'defects.summary', type: 'checkbox', fields: [ { title: '屋顶框架', type: 'checkbox', name: 'defects.summary.roofFraming', }, { title: '门廊支撑', type: 'checkbox', name: 'defects.summary.porchSupport', }, { title: '户外区域支撑', type: 'checkbox', name: 'defects.summary.alfrescoSupport', }, { title: '大门结构', type: 'checkbox', name: 'defects.summary.gateFeature', }, { title: '底层地板结构', type: 'checkbox', name: 'defects.summary.subfloorStructure', }, { title: '砖块', type: 'checkbox', name: 'defects.summary.brick', }, { title: '其他', type: 'textarea', }, ], }, ], }
父组件代码
const { register, handleSubmit, errors, setValue, getValues, control, watch, reset } = useForm({ shouldUnregister: false }) const { fields, append, remove, update } = useFieldArray({ control: control, name: defects.title, }) const onSubmit = async (data) => { const values = getValues() console.log('提交数据', data, values) const response = await fetch('/api/db/handler', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }) if (response.ok) { setSaved(true) setTimeout(() => { setSaved(false) }, 2000) } return response } return( <Defects data={defects} register={register} setValue={setValue} control={control} watch={watch} fields={fields} append={append} remove={remove} update={update} /> )
Defect组件代码
const Defects = ({ register, setValue, control, watch, data, fields, append, remove, update, }) => { useEffect(() => { update(); }, [register]); return ( <div> {fields.map((props, index) => { return ( <Disclosure defaultOpen open={true} as='div' key={props.id} className='border-b-4 p-4 border-gray-100' > {index > 0 ? ( <div className='flex items-center'> <Disclosure.Button className='text-xl uppercase underline flex items-center'> {`缺陷 ${index}`} <FaChevronDown className='ml-4 ui-open:rotate-180 ui-open:transform' /> </Disclosure.Button> <button className='bg-sky-400 text-white px-4 py-2 rounded mt-4 ml-12' onClick={() => remove(index)} > 删除 </button> </div> ) : ( <div className='flex items-center'> <Disclosure.Button className='text-xl uppercase underline flex items-center'> 缺陷列表 <FaChevronDown className='ml-4 ui-open:rotate-180 ui-open:transform' /> </Disclosure.Button> </div> )} {props.fields.map((field, i) => { return ( <Disclosure.Panel key={field.title + i} className='grid grid-cols-[minmax(min-content,.5fr)_minmax(300px,1fr)] my-4' > {field.sectionTitle && ( <h3 className='col-span-2 text-lg border-b-4 mb-2 border-b-gray-100 uppercase'> {field.sectionTitle} </h3> )} <h3>{field.title}</h3> {field.type == 'checkbox' && ( <Checkbox data={field} register={register} setValue={setValue} nestedIndex={index} fieldName={field.name} fieldIndex={i} /> )} </Disclosure.Panel> ); })} </Disclosure> ); })} </div> ); };
Checkbox组件代码
import React from 'react'; const Checkbox = ({ data, register, setValue, nestedIndex = null, fieldName = '', fieldIndex = null,}) => { return ( <div className='flex flex-col justify-start items-start'> {data.fields.map((item, index) => { return ( <div key={`${item.title}${index}`} className='flex flex-row items-center' > <label className='mr-2' htmlFor={item.name}> {item.title} </label> <input type={item.type} name={`Defects[${nestedIndex}].fields[${fieldIndex}].fields[${index}].${item.name}`} className={`${ item.type == 'textarea' && 'w-full' } border border-gray-100 p-2`} {...register( `Defects[${nestedIndex}].fields[${fieldIndex}].fields[${index}].${item.name}` )} /> </div> ); })} </div> ); }
问题根源分析
useFieldArray初始化缺失默认值:未给嵌套数组设置匹配结构的初始值,导致fields结构不匹配,无法正确绑定表单控件。- 表单控件name路径冗余:Checkbox组件中
item.name本身包含defects.location.interior这类前缀,拼接后路径变成Defects[0].fields[0].fields[0].defects.location.interior,结构混乱。 update方法调用时机错误:Defects组件中依赖register调用update(),但register是稳定引用,会触发不必要的无效更新。- 字段命名冲突:字段结构中的
name属性(如defects.location)和顶层数组名称Defects重复,导致表单数据结构混乱。
修复方案
1. 给useForm设置匹配的初始值
在父组件的useForm中添加默认值,对齐嵌套数组结构:
const { register, handleSubmit, errors, setValue, getValues, control, watch, reset } = useForm({ shouldUnregister: false, defaultValues: { Defects: [ { fields: [ { fields: [ { interior: false }, { roofSpace: false }, { other: '' } ] }, { fields: [ { roofFraming: false }, { porchSupport: false }, { alfrescoSupport: false }, { gateFeature: false }, { subfloorStructure: false }, { brick: false }, { other: '' } ] } ] } ] } })
2. 修正Checkbox组件的name路径
通过字符串截取获取纯字段名,避免冗余前缀:
const Checkbox = ({ data, register, setValue, nestedIndex = null, fieldName = '', fieldIndex = null,}) => { return ( <div className='flex flex-col justify-start items-start'> {data.fields.map((item, index) => { // 截取纯字段名,去掉前缀 const pureFieldName = item.name?.split('.').pop() || `other_${index}`; return ( <div key={`${item.title}${index}`} className='flex flex-row items-center' > <label className='mr-2' htmlFor={pureFieldName}> {item.title} </label> <input type={item.type} name={`Defects[${nestedIndex}].fields[${fieldIndex}].fields[${index}].${pureFieldName}`} className={`${ item.type == 'textarea' && 'w-full' } border border-gray-100 p-2`} {...register( `Defects[${nestedIndex}].fields[${fieldIndex}].fields[${index}].${pureFieldName}` )} /> </div> ); })} </div> ); }
3. 移除无效的useEffect
删除Defects组件中依赖register的useEffect,update方法仅在需要手动更新字段时调用:
// 移除以下代码 useEffect(() => { update(); }, [register]);
4. 统一字段命名规范(可选)
将字段结构中的name改为纯标识,避免和顶层数组名称冲突:
// 示例修改 { title: '缺陷位置:', name: 'location', type: 'checkbox', fields: [ { title: '室内', type: 'checkbox', name: 'interior', }, // ...其他字段 ] }
5. 确保useFieldArray的name与默认值一致
父组件中useFieldArray的name需和defaultValues中的顶层数组键完全匹配:
const { fields, append, remove, update } = useFieldArray({ control: control, name: 'Defects', })
内容的提问来源于stack exchange,提问作者Nayan Bhakhar
相关产品推荐
相关产品推荐

