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

React简易Todo应用切换isCompleted状态时出现TypeError报错

Fixing the "Cannot read property 'isCompleted' of undefined" Error in Your React Todo App

Hey there! Let's break down exactly what's causing that frustrating error and get your todo app working smoothly.

1. Missing isCompleted when adding new todos

First up: when you create a new todo in your addTodo method, you're only setting the text property—but forgetting to include isCompleted: false. That means any new todo you add doesn't have the isCompleted field at all. When your component tries to render item.isCompleted for these new items, it throws an error because the property doesn't exist on the object.

Fix: Update your addTodo method to include the isCompleted property, and use prevState to get the latest newTodo value (safer than relying on this.state directly):

addTodo = event => {
  event.preventDefault();
  this.setState(prevState => ({
    newTodo: "",
    todoList: [...prevState.todoList, { text: prevState.newTodo, isCompleted: false }]
  }));
};

2. Empty map function in toggleTodo

Right now, your toggleTodo method has an empty map callback—so when you call setState, it replaces your todoList with an array full of undefined values. That's the main reason you're seeing "Cannot read property 'isCompleted' of undefined" after trying to toggle a todo.

On top of that, using event.target.parentNode.id to get the todo ID is fragile. If someone clicks the text inside the toggle button (like "( )"), event.target might be a text node instead of the button, leading to incorrect parent node references. A better approach is to pass the todo's index directly from the render method.

Fix:
First, modify the TodoListTable to pass the index when triggering toggleTodo:

<button className="toggleTodo" onClick={() => props.toggleTodo(index)}>
  {item.isCompleted ? "(X)" : "( )"}
</button>

Then update the toggleTodo method to properly toggle the isCompleted state for the correct todo:

toggleTodo = (todoIndex) => {
  this.setState(prevState => ({
    todoList: prevState.todoList.map((todo, i) => {
      if (i === todoIndex) {
        // Create a new object with toggled isCompleted value
        return { ...todo, isCompleted: !todo.isCompleted };
      }
      // Return unchanged todos for other indices
      return todo;
    })
  }));
};

(You can remove event.preventDefault() here—buttons inside a list don't trigger form submissions by default.)

3. Bonus: Make removeTodo more reliable

Same issue with your removeTodo method—let's fix it to use the index directly instead of DOM nodes:
Update the delete button in TodoListTable:

<button className="deleteTodo" onClick={() => props.removeTodo(index)}>
  X
</button>

Then update the removeTodo method:

removeTodo = (todoIndex) => {
  this.setState(prevState => ({
    todoList: prevState.todoList.filter((_, i) => i !== todoIndex)
  }));
};

4. One more thing: Missing newTodo prop in Input

You were also missing the newTodo prop in your Input component, which would have caused the input field to not clear correctly after submitting a todo. Fix that in your App render method:

<Input addTodo={this.addTodo} handleChange={this.handleChange} newTodo={this.state.newTodo} />

Full Corrected Code

Here's the complete working code with all these fixes:

import React, { Component } from 'react';
const Input = (props) => {
  return (
    <form onSubmit={props.addTodo}>
      <input placeholder="What needs to be done?" value={props.newTodo} className="new-todo" type="text" onChange={props.handleChange} />
      <input type="submit" value="Submit" />
    </form>
  );
}
const TodoListTable = props => {
  return (
    <ul className="todo-list">
      {props.todoList.map((item, index) => {
        return (
          <li key={index}>
            <button className="toggleTodo" onClick={() => props.toggleTodo(index)}>
              {item.isCompleted ? "(X)" : "( )"}
            </button>
            <span className="textTodo">{item.text}</span>
            <button className="deleteTodo" onClick={() => props.removeTodo(index)}>
              X
            </button>
          </li>
        );
      })}
    </ul>
  );
};
const FilterListTable = props => {
  return <ul className="filters">
    <li>
      <a href="#/">All</a>
    </li>
    <li>
      <a href="#/active">Active</a>
    </li>
    <li>
      <a href="#/completed">Completed</a>
    </li>
  </ul>;
};
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      newTodo: "",
      todoList: [
        { text: "zrobić pranie", isCompleted: false },
        { text: "zrobić lunch", isCompleted: true },
        { text: "zrobić pranie", isCompleted: false }
      ]
    }
  }
  handleChange = event => {
    this.setState({ newTodo: event.target.value });
  };
  addTodo = event => {
    event.preventDefault();
    this.setState(prevState => ({
      newTodo: "",
      todoList: [...prevState.todoList, { text: prevState.newTodo, isCompleted: false }]
    }));
  };
  editTodo = event => {
    // event.preventDefault();
    // console.log(this.state.todoList);
  };
  toggleTodo = (todoIndex) => {
    this.setState(prevState => ({
      todoList: prevState.todoList.map((todo, i) => {
        if (i === todoIndex) {
          return { ...todo, isCompleted: !todo.isCompleted };
        }
        return todo;
      })
    }));
  };
  removeTodo = (todoIndex) => {
    this.setState(prevState => ({
      todoList: prevState.todoList.filter((_, i) => i !== todoIndex)
    }));
  };
  render() {
    return (
      <div className="todoapp">
        <h1>React Todo</h1>
        <Input addTodo={this.addTodo} handleChange={this.handleChange} newTodo={this.state.newTodo} />
        <TodoListTable todoList={this.state.todoList} toggleTodo={this.toggleTodo} removeTodo={this.removeTodo} />
        <hr />
        <FilterListTable />
        <br />
      </div>
    );
  }
}
export default App;

These changes should resolve all your errors and let you toggle, add, and delete todos without issues!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:25:43