如何使用对象数组中的 usestate 更新状态?

How to update state with usestate in an array of objects?

我在使用 React useState 挂钩时遇到了一些问题。我有一个带有复选框按钮的待办事项列表,我想将 'done' 属性 更新为 'true',它与 'clicked' 复选框按钮的 ID 具有相同的 ID。如果我 console.log 我的 'toggleDone' 运行它 returns 正确的 id。但是我不知道如何更新正确的 属性.

当前状态:

const App = () => {

  const [state, setState] = useState({
    todos: 
    [
        {
          id: 1,
          title: 'take out trash',
          done: false
        },
        {
          id: 2,
          title: 'wife to dinner',
          done: false
        },
        {
          id: 3,
          title: 'make react app',
          done: false
        },
    ]
  })

  const toggleDone = (id) => {
    console.log(id);
}

  return (
    <div className="App">
        <Todos todos={state.todos} toggleDone={toggleDone}/>
    </div>
  );
}

我想要的更新状态:

const App = () => {

  const [state, setState] = useState({
    todos: 
    [
        {
          id: 1,
          title: 'take out trash',
          done: false
        },
        {
          id: 2,
          title: 'wife to dinner',
          done: false
        },
        {
          id: 3,
          title: 'make react app',
          done: true // if I checked this checkbox.
        },
    ]
  })
const toggleDone = (id) => {
    console.log(id);
    // copy old state
    const newState = {...state, todos: [...state.todos]};
    // change value
    const matchingIndex = newState.todos.findIndex((item) => item.id == id);
    if (matchingIndex !== -1) {
       newState.todos[matchingIndex] = {
           ...newState.todos[matchingIndex], 
           done: !newState.todos[matchingIndex].done 
       }
    }
    // set new state
    setState(newState);
}

您需要像这样使用 spread operator:

const toggleDone = (id) => {
    let newState = [...state];
    newState[index].done = true;
    setState(newState])
}

您可以安全地使用 javascript 的数组映射功能,因为它不会修改 React 不喜欢的现有状态,并且它 returns 是一个新数组。该过程是循环遍历状态的数组并找到正确的 id。更新 done 布尔值。然后使用更新后的列表设置状态。

const toggleDone = (id) => {
  console.log(id);

  // loop over the todos list and find the provided id.
  let updatedList = state.todos.map(item => 
    {
      if (item.id == id){
        return {...item, done: !item.done}; //gets everything that was already in item, and updates "done"
      }
      return item; // else return unmodified item 
    });

  setState({todos: updatedList}); // set state to new object with updated list
}

编辑:更新代码以切换 item.done 而不是将其设置为 true。

我会使用 useState 而不是其他状态来创建 todos 数组,关键是创建 todos 数组的副本,更新它,并将其设置为新数组。 这是一个工作示例:https://codesandbox.io/s/competent-bogdan-kn22e?file=/src/App.js

const App = () => {
  const [todos, setTodos] = useState([
    {
      id: 1,
      title: "take out trash",
      done: false
    },
    {
      id: 2,
      title: "wife to dinner",
      done: false
    },
    {
      id: 3,
      title: "make react app",
      done: false
    }
  ]);

  const toggleDone = (e, item) => {
    const indexToUpdate = todos.findIndex((todo) => todo.id === item.id);
    const updatedTodos = [...todos]; // creates a copy of the array

    updatedTodos[indexToUpdate].done = !item.done;
    setTodos(updatedTodos);
  };

所有的答案都很好,但我会这样做

setState(prevState => {
    ...prevState,
    todos: [...prevState.todos, newObj]
})

这将安全地更新状态。也将保持数据完整性。这样也可以解决更新时的数据一致性问题

如果你想做任何条件,就这样做

setState(prevState => {
    if(condition){
        return {
            ...prevState,
            todos: [...prevState.todos, newObj]
        }
    }else{
        return prevState
    }
})

D.史密斯的回答很好,但可以重构为像这样更具声明性..

const toggleDone = (id) => {
 console.log(id);
 setState(state => {
     // loop over the todos list and find the provided id.
     return state.todos.map(item => {
         //gets everything that was already in item, and updates "done" 
         //else returns unmodified item
         return item.id === id ? {...item, done: !item.done} : item
     })
 }); // set state to new object with updated list
}