Home > OS >  How do you modify an object multiple times and return it in a spread operator?
How do you modify an object multiple times and return it in a spread operator?

Time:04-18

I'm working in React. I'm using useState to hold an object in state. To make one change to the object and return it I can use the spread operator like so ...

 const [formWizard, setFormWizard] = useState(FORM_CONTROLS.steps[index]);

...

 const addSection = section => {
...

   setFormWizard(wizard => ({
     ...wizard,
     wizard: formWizard.controls[14].group.push(...subjectAddressCopy.slice(0, 5))
   }));
}

But what if I need to make multiple changes? I've tried putting a comma between them, use &&, and wrapping it in an object (though I think that would change the data structure). In either event known of those things worked.

   setFormWizard(wizard => ({
      ...wizard,
      wizard: formWizard.controls[14].group.push(...subjectAddressCopy.slice(0, 5),
      formWizard.controls[14].trash) // how can I also make this change?
   }));

To be clear, I want to add the property of trash to my object and set it to true. How can I make two changes to formWizard and return it?

CodePudding user response:

The answer was kind of obvious. Posting this here in case anyone else comes across this question. In my setFormWizard function I was just returning an object so if I want to mutate the object more than once I can mutate it above the return statement and then just return it.

  setFormWizard(wizard => {
    wizard.controls[14].trash = true;
    return ({
      ...wizard,
      wizard: formWizard.controls[14].group.push(...subjectAddressCopy.slice(0, 5)),
    });
  });

CodePudding user response:

It's bad practice to mutate the state, which is what you do when you push to .controls[14].group. The reason for this is that in some cases React won't notice the change and thus not trigger re-render.

Also you should not have a new state depend on the former state, unless that state comes from the functional setState update. The reason for this is that the former state might not be up to date if multiple updates are queued up. In your suggestion you mix wizard (which is the old state you are allowed to rely on), with formWizard (which is the old state you can't rely on). Only use wizard here.

To update .controls[14].group and .controls[14].trash without mutation you can do the following

setFormWizard(wizard => {
  const controls = wizard.controls[14]
  const groups = [...controls.group, ...subjectAddressCopy.slice(0, 5)]
  return ({
    ...wizard,
    controls: [
      ...wizard.controls.splice(0,14), // keep first 14 entries 
      {
        ...controls,
        group: groups,
        trash: true
      } // modify the 15th entry
      ...wizard.controls.splice(15) // keep the succeeding entries
    ],
    wizard: groups.length
  });
});

I agree with Brandon and believe you don't want the result of the push operation in the wizard property. But since you seem certain that's what you want, wizard: groups.length gives you the same.

I use the spread operator throughout to create new arrays and objects. Here's an article on alternative approaches. https://medium.com/@kkranthi438/dont-mutate-state-in-react-6b25d5e06f42

  • Related