setState 不更新数组

时间:2021-03-11 03:29:01

标签: javascript reactjs state setstate array.prototype.map

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

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

1 个答案:

答案 0 :(得分:0)

问题

你正在改变你的状态对象。由于嵌套对象引用与渲染任何可能已更新的内容相同。换句话说,由于对象引用与之前的渲染相同,因此 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
    }
  })
}