RHF Field Array字段重复问题求助:输入时生成新UUID字段
React Hook Form Field Array 字段被重新创建问题
问题现象
使用RHF的Field Array功能时,填写字段数组中的字段会触发RHF用新UUID重建该字段;初始状态下filters是全为空字符串的对象数组,但输入内容后,值会被存入新生成的、所有属性为undefined的字段中。
代码问题分析
错误使用
field.id作为数组索引
代码中getValues('filters')[field?.id]是核心错误:filters是数组,只能用数字索引(0、1、2...)访问,而field.id是RHF生成的UUID字符串,用它访问数组会得到undefined,进而导致后续subFields判断异常,触发不必要的渲染逻辑,最终让RHF无法正确关联字段条目。Field命名不符合Field Array规范
所有Field组件的name属性用了filters.${field.id}.xxx,但RHF的Field Array是基于数组索引管理的,应该使用filters.${index}.xxx来关联数组中的对应项,否则RHF会识别为新的独立字段,生成新UUID。渲染周期内频繁调用
getValues
在fields.map中多次调用getValues会导致性能损耗,且容易获取到过期状态的值,应该用useWatch获取实时的表单值。
修正后的代码
import { useWatch } from 'react-hook-form'; const SegmentForm = () => { const { control, getValues } = useFormContext(); const { fields, append, remove} = useFieldArray({ control, name: 'filters' }); // 用useWatch获取实时的filters数组 const filters = useWatch({ control, name: 'filters' }); return ( <Stack gap={4} mt={2} mb={4}> <Field.Text name='name' label='Name' InputLabelProps={{ shrink: true }} /> <Field.Text multiline name='description' label='Description' InputLabelProps={{ shrink: true }} /> <Stack justifyContent='space-between' direction='row'> <Typography>Filters</Typography> <Button onClick={() => append({ field: '', operator: '', value: '' }) } variant='contained' startIcon={<Iconify icon='mingcute:add-line' />} > New filter </Button> </Stack> {fields.map((field, index) => { // 用数组索引获取当前字段值 const selectedField = filters[index]?.field; const subFields = FIELDS.find((e) => e.id === selectedField)?.values; return ( <Stack key={field.id} spacing={4}> <Stack spacing={4} justifyContent='space-between' direction='row' alignItems='center'> {/* 用index构建Field的name */} <Field.Select name={`filters.${index}.field`} label='Field'> {FIELDS.map((option) => ( <MenuItem key={option.id} value={option.id}> {option.label} </MenuItem> ))} </Field.Select> {filters.length > 1 && ( <Box> <IconButton color='inherit' onClick={() => remove(index)}> <Iconify icon='solar:trash-bin-trash-bold' /> </IconButton> </Box> )} </Stack> <Stack direction='row' spacing={4}> <Field.Select name={`filters.${index}.operator`} label='Operator'> {OPERATOR.map((option) => ( <MenuItem key={option.id} value={option.id}> {option.label} </MenuItem> ))} </Field.Select> {!subFields && ( <Field.Text name={`filters.${index}.value`} label='Value' InputLabelProps={{ shrink: true }} /> )} {!!subFields?.length && ( <Field.Select name={`filters.${index}.value`} label='Value'> {subFields.map((option) => ( <MenuItem key={option.id} value={option.id}> {option.label} </MenuItem> ))} </Field.Select> )} </Stack> {index !== filters.length - 1 && <Divider />} </Stack> ); })} </Stack> ); };
关键修正点说明
- 替换
field.id为数组index来构建Field的name属性,让RHF正确关联数组中的对应条目,避免生成新UUID。 - 使用
useWatch获取实时的filters数组,替代渲染周期内频繁调用getValues,提升性能同时确保获取最新状态。 - 所有依赖
filters长度的判断,改用filters.length替代getValues('filters')?.length,保持状态一致性。
内容的提问来源于stack exchange,提问作者ebillis
相关产品推荐
相关产品推荐

