React状态更新异常:tasks数组仅渲染最后一个元素
问题核心
你代码的bug非常明确:
- 你写的
changeTask方法用setTasks([task])直接把整个任务列表替换成了只有当前编辑项的单元素数组,只要状态更新触发,其余任务就会全部丢失。你注释掉这行代码后功能“正常”,本质是父组件状态根本没有被修改,自然不会出现列表被覆盖的问题。 - Task组件里的useEffect写法有缺陷:本地编辑状态一变就直接调用父组件更新方法,既没做任务匹配,还会导致每次键盘输入都触发全量重渲染,极端情况会触发更新死循环。
修复方法
React状态更新要遵循不可变数据原则,修改列表某一项时不能直接替换整个数组,要基于原数组遍历,匹配到对应id的项替换成新值,剩下的项保持不变。各组件修正后的代码如下:
PageTasks.jsx
const PageTasks = () => { const [tasks, setTasks] = useState([ { id: 1, nameTask: 'Продукты', descriptionTask: 'Масло', }, { id: 2, nameTask: 'д/з', descriptionTask: 'Сделать д/з', }, { id: 3, nameTask: 'Перестановка', descriptionTask: 'Помочь бабушке сделать перестановку', } ]) // 修正更新逻辑:匹配id替换对应任务 const changeTask = (updatedTask) => { setTasks(prev => prev.map(item => item.id === updatedTask.id ? updatedTask : item)) } return ( <Tasks setTasks={changeTask} tasks={tasks} /> ) } export default PageTasks;
Tasks.jsx
补全原代码缺失的根标签语法即可,逻辑不需要改动:
const Tasks = ({ tasks, setTasks }) => { return ( <> {tasks.map((task) => ( <Task setTasks={setTasks} key={task.id} task={task} /> ))} </> ) } export default Tasks;
Task.jsx
移除多余的useEffect,输入变化时同步更新本地状态和父组件状态即可:
const Task = ({ task, setTasks }) => { const [taskEdit, setTaskEdit] = useState(task) const updateTask = (newVal) => { const updated = { ...taskEdit, ...newVal } setTaskEdit(updated) setTasks(updated) } return ( <> <MyInput value={taskEdit.nameTask} onChange={e => updateTask({ nameTask: e.target.value })} /> <MyInput value={taskEdit.descriptionTask} onChange={e => updateTask({ descriptionTask: e.target.value })} /> </> ) }; export default Task;
可选优化:如果不需要输入实时同步到全局状态,可以加个保存按钮,编辑完成点击时再调用
setTasks,能大幅减少不必要的重渲染。
内容的提问来源于stack exchange,提问作者Андрей Димитриев
相关产品推荐
相关产品推荐

