简体   繁体   中英

React setState not updating checkbox checked state

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?

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM