Reputation: 631
I'm following a React beginners tutorial making a todo app as an example.
in the App.js, there is a handleChange method that will update the state whether the checkbox is checked or not, and passes it into the TodoItem component
class App extends React.Component {
constructor() {
super()
this.state = {
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
console.log(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 className="todo-list">
{todoItems}
</div>
)
}
}
export default App
TodoItem.js
function TodoItem(props) {
return (
<div className="todo-item">
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
it successfully displays the list and the console log correctly displays the checkbox clicked, however, the checkbox does not change. Can anyone tell me the problem?
Upvotes: 2
Views: 1537
Reputation: 827
I think you are running into a state mutation problem which is causing some unexpected behavior. The reason for this is because inside your if statement
within map
you are not returning your modified array item and you are actually modifying your state array and your new array.
How to fix: return your modified array item inside the if statement
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
return {
...todo,
completed: !todo.completed
}
}
return todo
})
return {
todos: updatedTodos
}
})
Or use a one liner with conditional (ternary) operator:
this.setState(prevState => ({
...prevState,
todos: prevState.todos.map(todo => todo.id === id ? { ...todo, ...{ completed: !todo.completed } } : todo)
}))
Look at this example I created in playground to get a better understanding:
I defined two arrays, one array uses map()
without the return and the other array is using map()
with the return.
Our goal is to keep our two arrays exactly the same and using map()
to create a new modified array. Look at the log results and notice how our initial array gets modified aswell. Our second todo item in this array should have a completed
value of true
but it gets changed to false
after our map()
which we would want to avoid. By returning our array item in the correct way we avoid this.
To get a better understanding of what state mutation is and how to avoid check this.
Upvotes: 5