React使用useState更新数组内对象值时多输入框卡顿如何优化?
优化方案
一、先修复现有代码的bug和冗余逻辑
- 你当前
addField方法直接修改了原state引用,会导致状态更新异常,同时所有任务的status数组共用同一份引用,修改单个任务的状态会影响所有任务,修复后的新增逻辑:
// 可直接删除无用的taskCount state和对应的useEffect,用test数组长度判断任务数量即可 const [test , setTest] = useState([]); const schema = { id : 0, choice : '', questName : '', location : '', time : '', linkedQuestB : '', linkedQuestA : '', status : [ {id : 'not_started' , label : 'not_started' , checked : false}, {id : 'in_progress' , label : 'in_progress' , checked : false}, {id : 'done' , label : 'done' , checked : false}, ], } const addField = () => { // 复制schema时单独复制status数组,避免引用共享 const newTask = {...schema, id: Date.now(), status: [...schema.status]} setTest(prev => [...prev, newTask]) } // 新增按钮直接调用addField即可 <img src = {plus} alt = {'添加任务'} onClick = {addField} />
- 删除更新逻辑里的冗余遍历,对象可直接通过键名取值赋值,不需要遍历所有key匹配:
const updateval = (index, target) => { setTest(prev => { // 只复制一次数组,仅修改对应下标的数据 const newList = [...prev] newList[index][target.id] = target.value return newList }) }
二、解决输入卡顿的核心优化:避免全量重渲染
你当前只要一个输入框修改,所有任务的输入组件都会跟着重渲染,是卡顿的核心原因,加组件缓存即可解决:
- 用
React.memo包裹你的TodoFieldSet和输入框组件,只有props变化时才会重渲染:
const TodoFieldSet = React.memo(({id, update, task}) => { // 原有组件逻辑 })
- 用
useCallback包裹更新函数,避免每次父组件渲染生成新的函数实例导致缓存失效:
const updateval = useCallback((index, target) => { setTest(prev => { const newList = [...prev] newList[index][target.id] = target.value return newList }) }, [])
- 渲染列表时不要用index作为key,用任务的唯一id作为key,避免列表顺序变化时的渲染异常:
{test.map((task, index)=>{ return ( <TodoFieldSet id={index} key={task.id} update={updateval} task={task} /> ) })}
三、超大任务量场景的极致优化
如果需要支持上百个任务同时编辑,可以把数组结构换成对象存储,通过任务id直接匹配对应任务,连数组复制的开销都可以省掉:
// 用对象存储所有任务,key为任务id,value为任务数据 const [taskMap, setTaskMap] = useState({}) const addField = useCallback(() => { const newId = Date.now() setTaskMap(prev => ({ ...prev, [newId]: {...schema, id: newId, status: [...schema.status]} })) }, []) const updateval = useCallback((taskId, target) => { setTaskMap(prev => ({ ...prev, [taskId]: { ...prev[taskId], [target.id]: target.value } })) }, []) // 渲染时转数组即可 {Object.values(taskMap).map(task => ( <TodoFieldSet id={task.id} key={task.id} update={updateval} task={task} /> ))}
内容的提问来源于stack exchange,提问作者findnate
相关产品推荐
相关产品推荐

