复选框未更改React组件中的状态

时间:2020-11-02 12:53:03

标签: javascript reactjs

我正在使用React创建一个待办事项应用程序。待办事项的数据如下:

const todoData = [
    {
        id: 1,
        text: "Empty bin",
        completed: true
    },
    {
        id: 2,
        text: "Call mom",
        completed: false
    }
]

现在,我有一个App组件,可以在其中导入数据并将其保存为状态。

import todoData from "./todoData"

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      todos: todoData,
    }

    this.handleChange = this.handleChange.bind(this)
  }
  ...

我还有一个handleChange方法,该方法应该将completed属性的值更改为其反值。例如:对于ID为1的待办事项,其文本值为“ Empty Bin”,完成为true,因此默认情况下将选中此复选框。但是,单击它时,completed应该为false,并且不应再单击该复选框。 由于某种原因,这不会发生,因此completed保持其默认布尔值且不会翻转。因此,单击复选框不会发生任何变化。

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

        return {
          todos: updatedTodos
        }
      })
}

在使用console.log之后,我意识到todo.completed实际上确实被更改为其相反的值,但是由于某些原因,即使在devtools map()中该值在返回a时已更新,它也未在updatedTodos中更改。存储在updatedTodos中的新数组。因此,状态不会更改,并且无法单击复选框

TodoItem功能组件与App组件位于一个单独的文件中,并包含todo元素的HTML。如下所示:

function TodoItem(props) {
    return (
        <div className="todo-item">
            <input type="checkbox"
            checked={props.task.completed}
            onChange={() => props.handleChange(props.task.id)}/>
            <p>{props.task.text}</p>
        </div>
    )
}

此外,TodoItem也已在App组件中呈现

render() {
    const todoArray = this.state.todos.map(task => <TodoItem key={task.id} 
      task={task} handleChange={this.handleChange}/>)
    return (
      <div className="todo-list">
        {todoArray}
      </div>
    )
  }

2 个答案:

答案 0 :(得分:0)

您好像在handleChange函数中对todo对象进行了变异

handleChange(id) {
    this.setState(prevState => {
      const updatedTodos = prevState.todos.map(todo => {
          if (todo.id === id) {
            //todo.completed = !todo.completed this is mutating
            // in react mutating a object will result in unexpected results like this.
            // so you have to create a new object based on the current todo and return it

              return {
                  ...todo,
                  completed: !todo.completed
              }
          }
          return todo
        })

        return {
          todos: updatedTodos
        }
    })
}

那里发生了什么事?

基本上,如果我们更改对象属性值(例如EX:completed)而不更改其正在更改的内存位置,则数组中的每个对象都指向一个内存位置,

通过执行todo.completed = !todo.completed,我们可以直接更改completed属性的值,但是todo对象仍然指向相同的内存位置,因此我们对对象进行了更改,并且反应不会对其进行响应,现在通过这样做

 return {
    ...todo,  // <- create a new object based on todo
    completed: !todo.completed  // <- change the completed property
}

我们根据待办事项{...todo}创建一个新对象(新对象=新内存位置),并更改completed => {...todo, completed: !todo.completed}的值,因为这指向新的内存位置反应将响应更改。

如果您不熟悉传播算子(...),请阅读here,不要忘记在此处查看传播对象文字部分

答案 1 :(得分:0)

您需要散布对象,然后对特定字段进行变异

        if (todo.id === id) {
            return {
              ...todo,
              completed: !todo.completed
            }
          }