setState 不更新数组

setState not updating array

我正在使用 reactjs 创建一个待办事项应用程序,并且我正在尝试在用户单击输入时更新我的​​待办事项列表。当用户单击时,我可以更改该项目,但该项目的状态不会更新。我不明白为什么状态没有更新。我认为它与 .map() 和将待办事项对象返回到新数组有关,但我不太清楚出了什么问题。有什么想法吗?

App.js

import React from 'react'
import TodoItem from './components/TodoItem'
import TodosData from './TodosData'
import './App.scss'

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      todoItems: TodosData
    }

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

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

  render() {
    const todosComponents = this.state.todoItems.map( item => {
      return <TodoItem key={item.id} todo={item} handleChange={this.handleChange}/>
    })
  
    return (
      <div className="TodoList">
        {todosComponents}
      </div>
    )
  }

}

export default App;

TodosItem.js

import React from 'react'

function TodoItem(props) {

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

export default TodoItem

问题

您正在改变您的状态对象。由于嵌套对象引用是相同的,因此 React 会阻止渲染任何可能已更新的内容。换句话说,由于对象引用与之前的渲染相同,因此 React 假定没有任何变化。

handleChange(id) {
  this.setState( prevState => {
    const updatedTodos = prevState.todoItems.map(todo => {
      if (todo.id === id) {
        todo.completed = !todo.completed // <-- object mutation!!
      }
      return todo
    })
    return {
      todoItems: updatedTodos
    }
  })
}

解决方案

除了浅复制数组外,您还需要浅复制您正在更新的任何嵌套状态。

handleChange(id) {
  this.setState( prevState => {
    const updatedTodos = prevState.todoItems.map(todo => {
      if (todo.id === id) {
        return {
          ...todo // <-- copy todo
          completed: !todo.completed, // <-- update propety
        }
      }
      return todo
    })
    return {
      todoItems: updatedTodos
    }
  })
}