React条件渲染任务列表报错:Cannot read properties of undefined (reading 'done')
Cannot read properties of undefined (reading 'done')错误排查 我学习React两个月,最近写了一个TaskList组件,根据任务的done状态(false为待完成,true为已完成)分组渲染任务。组件代码如下:
function TaskList() { const { tasks } = useContext(TaskContext); return tasks.length === 0 ? ( <h2>No hay tareas agregadas</h2> ) : ( <section className="task-list"> <div className="pending-task"> <h3>Tareas pendientes:</h3> <ul className="list"> {tasks.filter((t) => t.done === false).length === 0 ? ( <h4>No hay tareas por realizar</h4> ) : ( tasks .filter((t) => t.done === false) .map((t, i) => <TaskCard key={t.id} task={t} index={i} />) )} </ul> </div> <div className="done-task"> <h3>Tareas realizadas:</h3> <ul className="list"> {tasks.filter((t) => t.done === true).length === 0 ? ( <h4>No ha realizado ninguna tarea</h4> ) : ( tasks .filter((t) => t.done === true) .map((t, i) => <TaskCard key={t.id} task={t} index={i} />) )} </ul> </div> </section> ); }
测试时点击标记任务完成的按钮,会抛出错误:Uncaught TypeError: Cannot read properties of undefined (reading 'done'),错误指向tasks.filter((t) => t.done === false).length === 0这一行。刷新页面后应用恢复正常,且任务状态已经更新。我猜测可能是过滤时数组元素没有done属性,但不确定具体原因。
问题原因分析
状态更新过程中混入
undefined元素
点击标记完成按钮时,更新任务状态的逻辑可能出错:比如修改tasks数组时,因索引操作错误、解构赋值遗漏元素,或是异步更新的中间状态,导致新数组里出现了undefined元素。React重新渲染组件时,过滤操作访问t.done就会触发报错。状态更新违反不可变性原则
如果直接修改原数组或原任务对象(比如执行task.done = !task.done),而不是返回新数组和新对象,会导致React状态检测机制失效,渲染时可能出现数组元素异常的情况。Context状态初始化/更新的异步问题
若TaskContext的tasks状态是通过异步操作(如读取本地存储、API请求)初始化或更新的,在状态未完全就绪时,组件可能拿到不完整的数组(比如部分元素未初始化),进而引发渲染错误。
解决办法
过滤前先校验元素有效性
修改过滤条件,先检查t是否存在,再访问done属性,避免因undefined元素报错:tasks.filter((t) => t && t.done === false)严格遵循状态不可变性
更新tasks时必须返回新数组和新对象,比如标记任务完成的逻辑应写成:const toggleTaskDone = (taskId) => { setTasks(prevTasks => prevTasks.map(task => { if (task.id === taskId) { // 返回新对象,不修改原对象 return {...task, done: !task.done}; } return task; })); };给Context设置合理初始值
确保TaskContext的tasks初始值是空数组[],而非undefined。如果是异步加载任务,加载完成前可显示加载状态,避免组件拿到未就绪的状态。提取过滤逻辑减少重复计算
先统一处理有效任务再分组,既减少重复计算,也方便调试:function TaskList() { const { tasks } = useContext(TaskContext); // 先过滤掉无效元素 const validTasks = tasks.filter(t => t); const pendingTasks = validTasks.filter(t => t.done === false); const doneTasks = validTasks.filter(t => t.done === true); return validTasks.length === 0 ? ( <h2>No hay tareas agregadas</h2> ) : ( <section className="task-list"> <div className="pending-task"> <h3>Tareas pendientes:</h3> <ul className="list"> {pendingTasks.length === 0 ? ( <h4>No hay tareas por realizar</h4> ) : ( pendingTasks.map((t, i) => <TaskCard key={t.id} task={t} index={i} />) )} </ul> </div> <div className="done-task"> <h3>Tareas realizadas:</h3> <ul className="list"> {doneTasks.length === 0 ? ( <h4>No ha realizado ninguna tarea</h4> ) : ( doneTasks.map((t, i) => <TaskCard key={t.id} task={t} index={i} />) )} </ul> </div> </section> ); }
内容的提问来源于stack exchange,提问作者user20294238

