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

30,341

Solution 1

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. The process is to loop over the state's array and find the correct id. Update the done boolean. Then set state with the updated list.

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.

Solution 2

You need to use the spread operator like so:

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

Solution 3

D. Smith's answer is great, but could be refactored to be made more declarative like so..

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
}

Solution 4

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);
}
Share:
30,341
MFA86
Author by

MFA86

Updated on December 21, 2021

Comments

  • MFA86
    MFA86 over 2 years

    I'm having some trouble with the React useState hook. 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. If I console.log my 'toggleDone' function it returns the right id. But I have no idea how I can update the right property.

    The current 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:

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