【问题标题】:Checkbox not changing state in React component复选框未更改 React 组件中的状态
【发布时间】:2021-02-15 03:14:35
【问题描述】:

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

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 方法,它应该将已完成属性的值更改为其反向值。例如:对于 id 为 1 的待办事项,其文本值为“Empty Bin”并且已完成为 true,因此默认情况下会选中复选框。然而,当它被点击时,完成应该是假的并且复选框不应该再被点击。 由于某种原因,这不会发生,因此 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() 中表示该值在返回时更新存储在 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>
    )
  }

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    看起来你正在改变 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
            }
        })
    }
    

    那里发生了什么?

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

    通过todo.completed = !todo.completed我们直接改变了completed属性的值但是todo对象仍然指向相同的内存位置,所以我们改变对象并且react不会响应它,现在通过这样做

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

    我们基于 todo {...todo}(新对象 = 新内存位置)创建一个新对象,并且我们更改 completed => {...todo, completed: !todo.completed} 的值,因为它指向新的内存位置,react 会响应变化。

    如果您不熟悉扩展运算符 (...),请阅读 here,不要忘记查看 Spread in object literals 部分

    【讨论】:

    • 谢谢。您的解决方案修复了它。你能解释一下变异是什么意思吗?
    【解决方案2】:

    你需要传播你的对象然后改变特定的字段

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

    【讨论】:

      猜你喜欢
      • 2020-08-12
      • 1970-01-01
      • 2018-09-21
      • 2021-11-06
      • 2012-01-15
      • 2021-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多