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

Posted

技术标签:

【中文标题】如何在对象数组中使用 usestate 更新状态?【英文标题】:How to update state with usestate in an array of objects? 【发布时间】:2021-12-14 15:08:59 【问题描述】:

我在使用 React useState 挂钩时遇到了一些问题。我有一个带有复选框按钮的 todolist,我想将 'done' 属性更新为 'true',它的 id 与 'clicked' 复选框按钮的 id 相同。如果我 console.log 我的 'toggleDone' 函数,它会返回正确的 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.
        ,
    ]
  )

【问题讨论】:

如果您提供尝试设置状态的方式会有所帮助。 您需要使用修改后的状态调用setState()。你尝试过什么吗?如果有,结果如何?如果您还在为如何开始而苦苦挣扎,请查看map() 函数。 这能回答你的问题吗? Whats the best way to update an object in an array in ReactJS? 此外,使用钩子,无需将数组嵌套在对象中。可以多次调用useState,分别管理不同的状态值。 【参考方案1】:

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

【讨论】:

【参考方案2】:

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

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

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

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

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

【讨论】:

我将在哪里检查条件,例如当条件为真时更新嵌套项,其他情况相同【参考方案3】:

您需要像这样使用spread operator:

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

【讨论】:

This also mutates the current state, which is an anti-pattern in React.【参考方案4】:

我会使用 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);
  ;

【讨论】:

【参考方案5】:
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);

【讨论】:

【参考方案6】:

您可以安全地使用 javascript 的数组映射功能,因为它不会修改现有的状态,这是 react 不喜欢的,它会返回一个新数组。该过程是循环遍历状态的数组并找到正确的 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。

【讨论】:

非常感谢,这正是我想要的! 没问题!我很高兴它有所帮助。 我想在@bravemaster 发布的答案中指出一些事情。他们使用...state, todos: [...state.todos] 传播状态,这是一种很好的做法。使用我的解决方案,如果您要在状态中包含除todos 以外的任何内容,那么它将在setState 操作中丢失(它现在可以正常工作,因为您在状态中拥有的只是 todos 对象)。同时保持所有其他状态和更新待办事项的方法是setState(...state, todos: updatedList);

以上是关于如何在对象数组中使用 usestate 更新状态?的主要内容,如果未能解决你的问题,请参考以下文章

使用 prevState 和 useState 钩子更新对象内部数组中的对象

在 useEffect 中使用 useState 值而不使状态更新 useEffect

如何使用 useState 设置对象数组的状态?

React Hooks:使用useState更新状态不会立即更新状态[重复]

如何使用我的反应应用程序中的 useState 钩子给出的 setState 方法增加状态对象的选定索引的计数

在 useState 钩子中反应设置状态