I am new to react.js. I am following the video on youtube for learning react.js. I am working on simple event handling and stuck in some issue. I want to check/uncheck the checkbox when user performs onclick function on the checkbox. but somehow the returning array is not updated and checkbox is not actionable. I am pasting my code below:
App.js
import React from 'react'
import Header from './components/Header'
import Todolist from './components/Todolist'
import todosData from './data/todosData'
class App extends React.Component {
constructor(){
super()
this.state = {
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id){
this.setState(prevState => {
console.log(prevState.todos)
const updatedTodos = prevState.todos.map(todo => {
if(todo.id === id){
todo.completed = !todo.completed
}
return todo
})
console.log(updatedTodos)
return{
todos : updatedTodos
}
})
}
render(){
const todoItems = this.state.todos.map(item => <Todolist key={item.id} item={item} handleChange={this.handleChange} />)
return (
<div>
<Header />
{todoItems}
</div>
)
}
}
export default App;
TodoList.js
import React from 'react'
function Todolist(props){
return(
<div className='todo-item'>
<input type='checkbox'
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<span>{props.item.text}</span>
</div>
)
}
export default Todolist
CodePudding user response:
You are trying to mutate the original item/object of the array at this line!
todo.completed = !todo.completed
You can try to create a new object using Object.assign
or using spread. Both are fine but the spread way is preferable.
Using Object.assign
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
// Object.assign creates a new object!
const changedTodo = Object.assign({}, todo, {
completed: todo.id === id ? !todo.completed : todo.completed
});
return changedTodo;
});
return {
todos: updatedTodos
};
});
}
Using spread ...
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
const changedTodo = {
...todo,
completed: todo.id === id ? !todo.completed : todo.completed
};
return changedTodo;
});
return {
todos: updatedTodos
};
});
}
CodePudding user response:
As stated here, setState
runs twice in strict mode. The state updater function gets called twice with the same input. Which should be fine and produce the same output. But the problem here is you have reused the todo
object instead of creating a new one. Which in turn causes the completed
flag to be flipped twice.
You can change your updater function to create a new todo object every time:
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
return {
...todo,
completed: !todo.completed,
};
}
return todo;
});
return {
todos: updatedTodos,
};
});