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

IT技术 javascript reactjs use-state
2021-04-12 20:21:08

我在 React useState 钩子上遇到了一些麻烦。我有一个带有复选框按钮的待办事项列表,我想将 '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.
        },
    ]
  })
6个回答

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

没问题!我很高兴它有帮助。
2021-05-29 20:21:08
非常感谢,这正是我要找的!
2021-05-30 20:21:08
我想在@bravemaster 发布的答案中指出一些内容。他们传播了{...state, todos: [...state.todos]}良好实践的状态使用我的解决方案,如果您要包含todos状态中以外的任何内容,它将在setState操作中丢失(它现在可以正常工作,因为状态中只有 todos 对象)。保持所有其他状态并同时更新待办事项的方法是setState({...state, todos: updatedList});
2021-06-10 20:21:08

您需要像这样使用扩展运算符

const toggleDone = (id) => {
    let newState = [...state];
    newState[index].done = true;
    setState(newState])
}
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);
}

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

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

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

如果您想做任何条件,请这样做

setState(prevState => {
    if(condition){
        return {
            ...prevState,
            todos: [...prevState.todos, newObj]
        }
    }else{
        return prevState
    }
})
我将在这里检查条件,例如,当条件为真时更新嵌套项,否则相同
2021-06-12 20:21:08

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
}