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