You still don't want to mutate state. So if your state is an object, you'll want to create a new object and set with that. This may involve spreading the old state. For example:
const [person, setPerson] = useState({ name: 'alice', age: 30 });
const onClick = () => {
// Do this:
setPerson(prevPerson => {
return {
...prevPerson,
age: prevPerson.age + 1
}
})
// Not this:
//setPerson(prevPerson => {
// prevPerson.age++;
// return prevPerson;
//});
}
That said, using hooks you often no longer need your state to be an object, and can instead use useState multiple times. If you're not using objects or arrays, then copying is not needed, so spreading is also not needed.
const [name, setName] = useState('alice');
const [age, setAge] = useState(30);
const onClick = () => {
setAge(prevAge => prevAge + 1);
}
Answer from Nicholas Tower on Stack OverflowYou still don't want to mutate state. So if your state is an object, you'll want to create a new object and set with that. This may involve spreading the old state. For example:
const [person, setPerson] = useState({ name: 'alice', age: 30 });
const onClick = () => {
// Do this:
setPerson(prevPerson => {
return {
...prevPerson,
age: prevPerson.age + 1
}
})
// Not this:
//setPerson(prevPerson => {
// prevPerson.age++;
// return prevPerson;
//});
}
That said, using hooks you often no longer need your state to be an object, and can instead use useState multiple times. If you're not using objects or arrays, then copying is not needed, so spreading is also not needed.
const [name, setName] = useState('alice');
const [age, setAge] = useState(30);
const onClick = () => {
setAge(prevAge => prevAge + 1);
}
What it means is that if you define a state variable like this:
const [myThings, changeMyThings] = useState({cats: 'yes', strings: 'yellow', pizza: true })
Then you do something like changeMyThings({ cats: 'no' }), the resulting state object will just be { cats: 'no' }. The new value is not merged into the old one, it is just replaced. If you want to maintain the whole state object, you would want to use the spread operator:
changeMyThings({ ...myThings, cats: 'no' })
This would give you your original state object and only update the one thing you changed.
I am fairly new to React and I think I am missing an important concept about state.
I am trying to update a state variable, specifically an array. The following function successfully updates the array if I use Array.push(), but not if I use destructuring/the spread operator. What am I missing? Also, the template is completely non-reactive - even when the console.log statement displays the correct value, the HTML does not.
function MyComponent() {
const [selection, setSelection] = useState([]);
const handleSelection = (id) => {
// doesn't work
// let newSelection = [...selection, id];
let newSelection = selection;
newSelection.push(id);
// Prints the correct value when using Array.push() but not destructuring
console.log(selection);
setSelection(newSelection);
};
return (
{/* never changes */}
<pre>{JSON.stringify(selection, null, 2)}</pre>
)
}
EDIT: I thought this was a problem with how I was setting state on the selection variable, but it turns out the issue was with other parts of my code that are tangentially related to this. The problem was that I was trying to to loop over some data and generate a component for each datum, but I wasn't doing that correctly. I was setting a different state variable to hold these components, and the various pieces of state were out of sync. Here is a repro of the working code, where I am mapping the components correctly (in the template directly rather than setting state, which seems to be better practice). As you can see, spread/destructuring works perfectly as expected now.
{selection}
handleSelection("testId")}>CLICK ME ); };