React Map函数未随状态数组更新重渲染,删除功能异常
问题修复:Material UI Select + Formik withFormik 列表删除异常
问题根源
- 状态不可变性违反:直接用
push()/splice()修改原数组,破坏React状态不可变原则,导致状态更新异常。 - 闭包陷阱:删除时依赖当前
items变量,而非通过函数式更新获取最新状态,操作的可能是旧状态快照。 - index作为key的风险:元素删除后后续元素index会变化,React diff算法错误复用组件,导致删除对应元素错位。
修复步骤
1. 修正添加功能(确保状态不可变)
替换原handleClick,用函数式更新基于旧状态生成新数组,并给每个项添加唯一ID:
const handleClick = ()=>{ setItems(prevItems => [...prevItems, { id: Date.now(), // 生成唯一ID,替代index作为标识 value: values.name }]); }
2. 修正删除功能(用filter生成新数组)
替换原deleteItem,通过函数式更新获取最新状态,过滤掉目标项:
const deleteItem = (itemId)=>{ setItems(prevItems => prevItems.filter(item => item.id !== itemId)); }
3. 调整列表渲染的key与props传递
将循环中的index替换为唯一ID作为key和删除标识:
{items.map((item)=>{ return ( <List key={item.id} // 用唯一ID作为key id={item.id} valCountry={item.value} onDel={deleteItem} /> ); })}
完整修正后的Form组件代码
import React from 'react' import { withFormik } from 'formik'; import { FormControl , Select, InputLabel, MenuItem, Fab } from '@mui/material' import AddIcon from '@mui/icons-material/Add' import { useState } from 'react'; import List from './List' const Form = (props) => { const[items, setItems]= useState([]); const{ values, handleChange, handleSubmit }= props; const handleClick = ()=>{ setItems(prevItems => [...prevItems, { id: Date.now(), value: values.name }]); } const deleteItem = (itemId)=>{ setItems(prevItems => prevItems.filter(item => item.id !== itemId)); } return ( <div className='main'> <FormControl sx={{ m: 1, minWidth: 250 }} size="small" onSubmit={handleSubmit}> <InputLabel id="country-label">Country</InputLabel> <Select labelId="Country-label-id" id="countries" value={values.name} label="Country" name= "name" onChange={handleChange} > <MenuItem value={9}></MenuItem> <MenuItem value={10}>India</MenuItem> <MenuItem value={20}>Canada</MenuItem> <MenuItem value={30}>Albania</MenuItem> <MenuItem value={40}>Zambia</MenuItem> <MenuItem value={50}>USA</MenuItem> <MenuItem value={60}>Poland</MenuItem> </Select> </FormControl> <Fab size='small' color="primary" aria-label="add" id='button'> <AddIcon onClick={handleClick}/> </Fab> <ul> {items.map((item)=>{ return ( <List key={item.id} id={item.id} valCountry={item.value} onDel={deleteItem} /> ); })} </ul> </div> ); } const MySelectField = withFormik({ mapPropsToValues: ()=>({name: ''}), enableReinitialize: true, handleSubmit: (values, { setSubmitting }) => { setTimeout(() => { console.log(JSON.stringify(values, null, 2)); setSubmitting(false); }, 1000); }, })(Form) export default MySelectField;
补充说明
之前用纯React Hooks正常,是因为当时状态更新写法更规范,没有直接修改原数组。集成Formik后状态更新方式出错,加上index作为key的隐患,最终触发删除异常,本质和Formik无关,是React状态管理的基础问题。
内容的提问来源于stack exchange,提问作者Wazowski YT
相关产品推荐
相关产品推荐

