Skip to content
Advertisement

How do I add a handlechange to a todo app in react?

I’m new to react and making a todo app.

I have added an event listener(handleChange) to flip the todosData.completed from true to false and vice versa hereby allowing the user to check and uncheck the checkbox.

How can I rectify this. Here is my code:

 class App extends React.Component {
  constructor() {
    super();
    this.state = {
      todos: todosData
    }
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(id) {
    this.setState(prevState => {
      const updatedTodos = prevState.todos.map(todo => {
        if (todo.id === id) {
          todo.completed = !todo.completed;
        }
        return todo;
      });

      return {
       todos : updatedTodos
      };
    });
  }

  render() {
    const todoItems = this.state.todos.map(item => (
      <TodoItem
        key={item.id}
        item={item}
        handleChange={this.handleChange}/>
    ));

    return(
      <div>{todoItems}</div>
    );
  }
}

export default App;

function TodoItem(props) {
  return (
    <div>
      <input 
        type = 'checkbox' checked = {props.item.completed}
        onChange = {() => props.handleChange(props.item.id)} />
      <p> {props.item.task} </p>
    </div>
  );
}

export default TodoItem;

Can someone tell me what I am missing?

Advertisement

Answer

As pointed by @ChrisG in the comments, this happens when your component is wrapped inside <React.StrictMode>. If you created your application using Create React App this is done by default, check it on your index.js file.

StrictMode is a development feature that forces you to write better code. It only affects your development build, so your code should probably work if built and run in production mode.

One of the things that StrictMode does is to run your setState method twice, to make sure that you are not relying on it running only one time. So the first time it runs you invert your todo.completed as expected, and in the second time it reverts it back to the original value.

What this is telling you is that your handleChange function is actually updating state with a method that is not pure, since you actually change the todo object inside your prevState.todos.map.

What you need to do instead is to return a new todo object which will be a copy of the other, only with the completed property modified, like below:

handleChange(id) {
  this.setState(prevState => {
    const updatedTodos = prevState.todos.map(todo => {
      return {
        id: todo.id,
        task: todo.task,
        completed: todo.id === id ? !todo.completed : todo.completed
      };
    });

    return {
     todos: updatedTodos
    };
  });
}

Or using ES6 spread operator, like below:

handleChange(id) {
  this.setState(prevState => (
    {
      todos: prevState.todos.map(todo => (
        {...todo, completed: todo.id === id ? !todo.completed : todo.completed}
      ))
    }
  ));
}

I believe this is a better solution than Chris G suggestion to remove <React.StrictMode>, since StrictMode actually helps you to write better code.

Advertisement