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

React Todo列表删除报错:Cannot read properties of undefined (reading 'id')

React Todo组件删除功能报错:Cannot read properties of undefined (reading 'id')

开发React组件Addtask实现Todo列表功能时,点击删除按钮(X图标)触发错误:Cannot read properties of undefined (reading 'id'),无法完成任务删除。相关代码及错误栈如下:

组件代码

import { type } from '@testing-library/user-event/dist/type';
import React, { useState } from 'react';
import './clint.css'

export default function Addtask() {
  const [task, setTask] = useState([]);
  const [text, setText] = useState('');
  const obj = [{ task: text.trim(), id: task.length }];

  const handleSubmit = async (event) => {
    event.preventDefault();
    try {
      setTask([...task, obj[obj.length-1].task]);
      const resp = await fetch(
        'http://localhost:5000/addtask',
        {
          method: 'POST',
          body: JSON.stringify(task),
          headers: {
            'content-type': 'application/json'
          }
        }
      );
      if (resp.status === 200) {
        console.log('task added successfully');
      } else {
        console.log("unable to post");
      }          
    } catch(error) {
      console.log("error");
    }
  }

  function change(e) {
    const ip = e.target.value;
    setText(ip);
  }

  return (
    <div>
      <h1>add user component</h1>
 
      <span>Add Task</span>
      <input onChange={change} placeholder='add a task' />
      <button type='submit' onClick={handleSubmit}>submit</button>
      <ul>
        {task.map((tasks, i) => 
          <li key={i}>
            {tasks}
            <button
              className='delete'
              onClick={() => {
                const removeItem = task.filter(() => {
                  return obj[i].id !==  i;
                });
                setTask(removeItem);
                console.log(i)
              }}
            >
              <b>X</b>
            </button>
          </li>
        )}                
      </ul>
    </div>
  )
}

错误信息

Cannot read properties of undefined (reading 'id')
TypeError: Cannot read properties of undefined (reading 'id')
  at http://localhost:3000/main.158d69bbe90ec71b22a6.hot-update.js:94:29
  at Array.filter (<anonymous>)
  at onClick (http://localhost:3000/main.158d69bbe90ec71b22a6.hot-update.js:93:37)
  at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:29605:18)
  at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:29649:20)
  at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:29706:35)
  at invokeGuardedCallbackAndCatchFirstError 
(http://localhost:3000/static/js/bundle.js:29720:29)
  at executeDispatch (http://localhost:3000/static/js/bundle.js:33864:7)
  at processDispatchQueueItemsInOrder (http://localhost:3000/static/js/bundle.js:33890:11)
  at processDispatchQueue (http://localhost:3000/static/js/bundle.js:33901:9)

问题分析与修复

核心问题

  1. obj的错误使用:obj是组件每次渲染时创建的仅含一个元素的数组,删除时访问obj[i],当列表项索引i>=1时,obj[i]必然是undefined,导致读取id报错。
  2. 任务存储格式错误:当前task数组仅存储任务字符串,没有保留唯一标识id,删除逻辑依赖索引和临时obj完全不成立。
  3. 提交逻辑错误:提交时把旧的task数组传给后端,而非新添加的任务;且没有清空输入框。

修复后的代码

import React, { useState } from 'react';
import './clint.css'

export default function Addtask() {
  // 存储带id的任务对象数组
  const [tasks, setTasks] = useState([]);
  const [text, setText] = useState('');

  const handleSubmit = async (event) => {
    event.preventDefault();
    const trimmedText = text.trim();
    if (!trimmedText) return;

    // 创建带唯一id的任务对象
    const newTask = {
      id: Date.now(), // 用时间戳生成唯一id
      content: trimmedText
    };

    try {
      // 更新本地状态
      setTasks(prevTasks => [...prevTasks, newTask]);
      // 提交新任务给后端
      const resp = await fetch(
        'http://localhost:5000/addtask',
        {
          method: 'POST',
          body: JSON.stringify(newTask),
          headers: {
            'content-type': 'application/json'
          }
        }
      );
      if (resp.ok) {
        console.log('task added successfully');
        // 清空输入框
        setText('');
      } else {
        console.log("unable to post");
        // 提交失败时回滚状态
        setTasks(prevTasks => prevTasks.filter(task => task.id !== newTask.id));
      }          
    } catch(error) {
      console.log("error:", error);
      // 报错时回滚状态
      setTasks(prevTasks => prevTasks.filter(task => task.id !== newTask.id));
    }
  }

  const handleDelete = (taskId) => {
    // 根据id过滤删除对应任务
    setTasks(prevTasks => prevTasks.filter(task => task.id !== taskId));
    // 可选:调用后端删除接口
    // fetch(`http://localhost:5000/deletetask/${taskId}`, { method: 'DELETE' })
    //   .catch(err => console.log("delete error:", err));
  }

  function change(e) {
    setText(e.target.value);
  }

  return (
    <div>
      <h1>add user component</h1>
 
      <span>Add Task</span>
      <input onChange={change} value={text} placeholder='add a task' />
      <button type='submit' onClick={handleSubmit}>submit</button>
      <ul>
        {tasks.map((task) => 
          <li key={task.id}>
            {task.content}
            <button
              className='delete'
              onClick={() => handleDelete(task.id)}
            >
              <b>X</b>
            </button>
          </li>
        )}                
      </ul>
    </div>
  )
}

关键修复点

  • 将task数组改为存储包含id和content的对象,确保每个任务有唯一标识。
  • 删除逻辑通过任务的id进行过滤,不再依赖索引和临时变量obj。
  • 提交时使用函数式更新状态,避免闭包捕获旧状态的问题。
  • 添加输入框内容清空逻辑,优化用户体验。
  • 提交失败/报错时添加状态回滚,保证本地数据一致性。

内容的提问来源于stack exchange,提问作者rohit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 06:17:03