React与Mongo:基于条件加载多对象状态数组遇覆盖问题
问题分析与修复方案
问题根源
- 异步更新+闭包陷阱:你在循环里反复调用
setCompletes/setStarted/setTodos,但React的状态更新是异步的。每次调用时,函数捕获的completes/started/todos都是loadTasks执行时的初始状态值,导致每次更新都是在初始数组(要么是你预设的结构对象数组,要么是刚清空的空数组)上添加当前task,最终只会保留最后一个符合条件的任务,前面的都被覆盖。 - 初始state冗余:初始化三个状态时塞入的结构对象,会导致初始数组存在无效空结构,后续清空操作和循环更新还会因异步特性产生冲突。
修复代码
第一步:修正初始state定义
将三个状态初始化为空数组(JavaScript无需预设结构,TypeScript才需要类型声明):
const [started, setStarted] = useState([]) const [todos, setTodos] = useState([]) const [completes, setCompletes] = useState([])
第二步:重构loadTasks函数
先把所有任务分类到临时数组,再一次性更新状态,彻底避开异步和闭包问题:
function loadTasks(tasksGrabed) { // 先分类所有任务 const newCompletes = [] const newStarted = [] const newTodos = [] for (const task of tasksGrabed) { if (task.complete) { newCompletes.push(task) console.log(`${task.title} complete`) } else if (task.started) { newStarted.push(task) console.log(`${task.title} started`) } else { newTodos.push(task) console.log(`${task.title} todos`) } } // 一次性更新三个状态 setCompletes(newCompletes) setStarted(newStarted) setTodos(newTodos) }
也可以用数组filter方法简化代码:
function loadTasks(tasksGrabed) { const newCompletes = tasksGrabed.filter(task => task.complete) const newStarted = tasksGrabed.filter(task => !task.complete && task.started) const newTodos = tasksGrabed.filter(task => !task.complete && !task.started) setCompletes(newCompletes) setStarted(newStarted) setTodos(newTodos) // 可选日志输出 newCompletes.forEach(task => console.log(`${task.title} complete`)) newStarted.forEach(task => console.log(`${task.title} started`)) newTodos.forEach(task => console.log(`${task.title} todos`)) }
修复逻辑说明
先完成所有数据分类,再一次性更新状态,既避免了闭包导致的状态引用过时问题,又减少了React重新渲染的次数,性能更优。
内容的提问来源于stack exchange,提问作者airin
相关产品推荐
相关产品推荐

