Case 1:
const [present, setPresent] = useState([]);
useEffect(() => {
for (var j = 1; j <= totalPeriod; j++) {
setPresent([
...present,
{
period: j,
present: true,
},
]);
}
}, []);
Case 2:
const [present, setPresent] = useState([]);
let createPresent = [];
for (var j = 1; j <= totalPeriod; j++) {
createPresent = [
...createPresent,
{
period: j,
present: true,
},
]
}
useEffect(() => {
setPresent(createPresent);
}, []);
When I am trying to update the present state using loop in inside useEffect() in Case 1, present state is not updating. But when I am separately using loop outside the useEffect() and creating an array which I am then assigning to present state in case 2, the present state is getting updated.
What is the reason behind this? Why present state is not updating in Case 1?
In the below case, your present state is not the result of each subsequent state update but rather the initial one which you had which is []. React will batch these updates and not make them happen synchronously so effectively there will be just one state update with present updated to latest entry in your for loop.
const [present, setPresent] = useState([]);
useEffect(() => {
for (var j = 1; j <= totalPeriod; j++) {
setPresent([
...present,
{
period: j,
present: true,
},
]);
}
}, []);
In the below case, you are first assembling a createPresent array with all the values you need and finally calling the state updator function i.e. setPresent to set the state.
const [present, setPresent] = useState([]);
let createPresent = [];
for (var j = 1; j <= totalPeriod; j++) {
createPresent = [
...createPresent,
{
period: j,
present: true,
},
]
}
useEffect(() => {
setPresent(createPresent);
}, []);
In order to achieve the second behaviour with first, you can make use of the state updator callback which holds the previous state as the argument like so :-
const [present, setPresent] = useState([]);
useEffect(() => {
for (let j = 1; j <= totalPeriod; j++) {
setPresent(prevState=>[
...prevState,
{
period: j,
present: true,
},
]);
}
}, []);
Here also state update is batched but previous state is factored in before updating the next one.
When I say a batched state update, I mean there will only be a single render. You can verify that by doing console.log('render') in your component's function body.
Note the use of let instead of var here since let is scoped you will get the accurate value for the variable j.
The assumption you're making in Case 1 is that the setPresent call is synchronous and that it updates present immediately, while in reality state updates in React are almost always asynchronous. Thus the present variable inside for (var j = 1; j <= totalPeriod; j++) { will be equal to the original value, which is an empty array []. So in essence, you're setting the state to this over and over:
[
...[],
{
period: j,
present: true,
},
]
This will result in your state being updated to the last array the for loop creates. So if totalPeriod is equal to 5, your state will end up being [{period: 5, present: true}] instead of [{period: 1, present: true}, {period: 2, present: true}, ... ]
In Case 2, everything is very straightforward, you assemble the array without messing with state variables and then set the state in one shot. Just like you're supposed to in this case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With