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.