简体   繁体   English

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

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

I'm having some trouble with the React useState hook.我在使用 React useState 挂钩时遇到了一些问题。 I have a todolist with a checkbox button and I want to update the 'done' property to 'true' that has the same id as the id of the 'clicked' checkbox button.我有一个带有复选框按钮的 todolist,我想将 'done' 属性更新为 'true',它的 id 与 'clicked' 复选框按钮的 id 相同。 If I console.log my 'toggleDone' function it returns the right id.如果我 console.log 我的 'toggleDone' function 它会返回正确的 ID。 But I have no idea how I can update the right property.但我不知道如何更新正确的属性。

The current state:当前的 state:

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>
  );
}

The updated state I want:我想要的更新后的 state:

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.
        },
    ]
  })

You can safely use javascript's array map functionality since that will not modify existing state, which react does not like, and it returns a new array.您可以安全地使用 javascript 的数组 map 功能,因为它不会修改现有的 state,它的反应不喜欢,它会返回一个新数组。 The process is to loop over the state's array and find the correct id.该过程是循环遍历状态的数组并找到正确的 id。 Update the done boolean.更新done的 boolean。 Then set state with the updated list.然后使用更新的列表设置 state。

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
}

Edit: updated the code to toggle item.done instead of setting it to true.编辑:更新代码以切换item.done而不是将其设置为 true。

You need to use the spread operator like so:您需要像这样使用扩展运算符

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

D. Smith's answer is great, but could be refactored to be made more declarative like so.. D. Smith 的回答很棒,但可以重构为更具声明性,就像这样..

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
}
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);
}

All the great answers but I would do it like this所有很好的答案,但我会这样做

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

This will safely update the state safely.这将安全地更新 state。 Also the data integrity will be kept.也将保持数据完整性。 This will also solve the data consistency at the time of update.这也将解决更新时的数据一致性。

if you want to do any condition do like this如果您想做任何条件,请这样做

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

Something similar to D. Smith's answer but a little more concise:类似于D. Smith 的回答,但更简洁一些:

const toggleDone = (id) => {

  setState(prevState => {
            // Loop over your list
            return prevState.map((item) => {
                // Check for the item with the specified id and update it
                return item.id === id ? {...item, done: !item.done} : item
            })
        })
}

I would create just the todos array using useState instead of another state, the key is creating a copy of the todos array, updating that, and setting it as the new array.我将使用 useState 而不是另一个 state 创建 todos 数组,关键是创建 todos 数组的副本,更新它,并将其设置为新数组。 Here is a working example: https://codesandbox.io/s/competent-bogdan-kn22e?file=/src/App.js这是一个工作示例: 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);
  };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM