react-hook-form useFieldArray删除异常:误删最后一个元素而非目标元素
React Hook Form useFieldArray 删除元素异常:总是删除最后一个元素
在使用react-hook-form的useFieldArray时遇到问题:点击删除按钮时,并未删除目标元素,反而总是删除最后一个元素。以下是相关代码:
export const isThisAFieldsArrayContext = React.createContext(false) export default function FieldsArray({ control, formNamePrefix, register, inputsToRender, addNewDisabled = false, watchConfig, ...props }) { const classes = useStyles() const { append, remove } = useFieldArray({ control, shouldUnregister: false, name: formNamePrefix, }) const additionalProps = useWatchForAdditionalProps(watchConfig) function handleRemove(index) { remove(index) } return ( <isThisAFieldsArrayContext.Provider value={true}> <Grid item xs={12}> <List> {control.getValues(formNamePrefix)?.map((item, index) => { console.log({ item }) return ( <ListItem key={index} className={classes.listItem}> <DynamicFormFields {...props} register={register} fieldsKey={`${formNamePrefix}.${index}`} formFields={inputsToRender} control={control} // defaultValue={item.defaultValue} /> <DeleteButton variant="contained" onClick={() => handleRemove(index)} aria-label="delete" className={classes.deleteButton} > <DeleteIcon color="secondary" fontSize="small" /> </DeleteButton> </ListItem> ) })} </List> <Button variant="contained" onClick={() => append({})} aria-label="add" color="secondary" className={classes.addButton} disabled={addNewDisabled || additionalProps.disabled} > <AddIcon color="primary" /> </Button> </Grid> </isThisAFieldsArrayContext.Provider> ) }
问题原因
- 使用数组索引作为React元素key:删除元素后,React的diff算法会误判元素身份。比如删除索引1的元素后,原索引2的元素会变为索引1,React会认为这是原索引1的元素仅内容更新,最终导致删除逻辑与UI不匹配。
- 非响应式的列表渲染方式:直接通过
control.getValues获取数组渲染,无法同步表单状态的实时变化,导致UI更新滞后。
解决方案
核心修改点
- 改用
useFieldArray返回的fields数组渲染列表(响应式,自动同步状态) - 给列表元素设置唯一稳定的key,避免使用索引,建议用元素唯一标识(如自定义id)
修改后的代码示例
// 若使用uuid需先安装:npm install uuid import { v4 as uuidv4 } from 'uuid'; export const isThisAFieldsArrayContext = React.createContext(false) export default function FieldsArray({ control, formNamePrefix, register, inputsToRender, addNewDisabled = false, watchConfig, ...props }) { const classes = useStyles() // 获取useFieldArray返回的fields数组 const { append, remove, fields } = useFieldArray({ control, shouldUnregister: false, name: formNamePrefix, }) const additionalProps = useWatchForAdditionalProps(watchConfig) function handleRemove(index) { remove(index) } return ( <isThisAFieldsArrayContext.Provider value={true}> <Grid item xs={12}> <List> {/* 用fields数组渲染,以field.id作为key */} {fields.map((field, index) => { return ( <ListItem key={field.id} className={classes.listItem}> <DynamicFormFields {...props} register={register} fieldsKey={`${formNamePrefix}.${index}`} formFields={inputsToRender} control={control} /> <DeleteButton variant="contained" onClick={() => handleRemove(index)} aria-label="delete" className={classes.deleteButton} > <DeleteIcon color="secondary" fontSize="small" /> </DeleteButton> </ListItem> ) })} </List> <Button variant="contained" // 添加元素时注入唯一id,保证key稳定 onClick={() => append({ id: uuidv4() })} aria-label="add" color="secondary" className={classes.addButton} disabled={addNewDisabled || additionalProps.disabled} > <AddIcon color="primary" /> </Button> </Grid> </isThisAFieldsArrayContext.Provider> ) }
补充说明
fields数组是useFieldArray提供的响应式数据,会自动同步表单数组的增删改变化,确保UI与状态一致。- 若不想引入uuid,也可以用
Date.now()生成临时唯一id,或者使用后端返回的唯一标识(如果有)。
内容的提问来源于stack exchange,提问作者Foris
相关产品推荐
相关产品推荐

