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.
What is the reason I have to use spread operator in React hooks?
reactjs - Spread operator in React .setState() in the useState Hook - Stack Overflow
Using the spread operator in setState hook
Need help with useState hook - can't update an array using the spread operator
Currently I'm doing this:
setContacts([...contacts, { newContact }]);
That results in newContact being added to the end of the contacts array. I'm wondering if I do
setContacts([{newContact }, ...contacts ]);
would newContact be added to the beginning of the contacts array, which is what I want?
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 ); };Reason is how you're accessing previous value to add new element:
for (let i = 0; i < data.breakouts.length; i++) {
data.breakouts[i].start = new Date(data.breakouts[i].start);
setFields({
...fields,
[data.breakouts[i].name]: data.breakouts[i].start
})
}
since fields will be updated only on next render, you are adding single element(but each loop it's different element) to the array.
The same would be without hooks/react:
const start = [];
for(let i = 0; i< 5; i++) console.log([...start, i]);
This will never output [0,1,2,3,4].
What can you do.
Option 1. Collect data in interm variable:
const temp = {};
for (let i = 0; i < data.breakouts.length; i++) {
data.breakouts[i].start = new Date(data.breakouts[i].start);
data.breakouts[i].end = new Date(data.breakouts[i].end);
temp[data.breakouts[i].name] = data.breakouts[i].start
}
setFields({
...fields,
...temp
});
Option 2. Use functional version of setter as an accumulator:
for (let i = 0; i < data.breakouts.length; i++) {
data.breakouts[i].start = new Date(data.breakouts[i].start);
setFields(({fields: prevFields}) => ({
...prevFields,
[data.breakouts[i].name]: data.breakouts[i].start
}))
}
I'd prefer first option for 2 reasons:
- it would be better finally move data transform closer to call point, so instead of
axios.getthere would be someAPI.getMeaningfullThingsreturning structure you need; so it's easier to refactor - maybe slighter more code shows better what's going on(it's just a transforming, not a filtering or any React-specific thing)
If I understand correctly, you have problem with setState from first example. Te reason is that you are calling setState inside of a loop, and you are trying to spread the old state, but the state doesn't get set before your loop is done, so you have to accumulate everything you want to put into your state inside the loop and set it at the end.