You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React条件渲染任务列表报错:Cannot read properties of undefined (reading 'done')

React任务状态切换时出现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属性,但不确定具体原因。


问题原因分析

  1. 状态更新过程中混入undefined元素
    点击标记完成按钮时,更新任务状态的逻辑可能出错:比如修改tasks数组时,因索引操作错误、解构赋值遗漏元素,或是异步更新的中间状态,导致新数组里出现了undefined元素。React重新渲染组件时,过滤操作访问t.done就会触发报错。

  2. 状态更新违反不可变性原则
    如果直接修改原数组或原任务对象(比如执行task.done = !task.done),而不是返回新数组和新对象,会导致React状态检测机制失效,渲染时可能出现数组元素异常的情况。

  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 05:02:45