⚡️ Updating State Based on the Previous State in React ⚡️
When working with React’s useState hook, it’s important to understand the asynchronous nature of state updates.
🔧 Setup Challenge: Set up a state value and a button. Add functionality to increase the value by 1.Log the state value right after calling setCount.
This behavior can lead to a discrepancy between the rendered value and the logged output value, where the logged value is one step behind the rendered value. For example, when the rendered value is 1, the logged output value may be 0. Similarly, when the rendered value is 2, the logged output value may be 1, and so on. 😕
For example, when the rendered value is 1, the logged output value may be 0. Similarly, when the rendered value is 2, the logged output value may be 1, and so on. 😕
Keep in mind that the state update function setState
does not immediately mutate the state. Instead, it schedules an update to the state and tells React that it needs to re-render the component. The actual state update will be performed as part of the next rendering cycle. ⏳
Let’s examine an example code snippet to illustrate this behavior: 😊
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
// be careful it's the old value
setCount(count + 1);
console.log(count);
// so if you have any functionality
// that relies on the latest value
// it will be wrong !!!
};
return (
<div>
<h1>you click {count} times</h1>
<button onClick={handleClick}>button</button>
</div>
);
}
export default App;
🧐 Why did this happen?
When you call setCount(count + 1)
, the state update is scheduled but not immediately applied. The console.log(count)
statement is executed before the state update takes place, and it logs the current value of count
at that moment. Since the state update is asynchronous and doesn't happen instantly, the logged value is still the previous value of count
before the update.
💡 Solution: Updating State Based on the Previous State
If you want to update the state immediately and synchronously, you can pass a function to setState that receives the previous state as an argument and returns the new state. For example:
setCount((previousValue) => {
return previousValue + 1;
});
This can be useful if you need to update the state based on the previous state or if you need to update the state synchronously.
🔗 Read more about useState in React
🌟 Use case where this lazy initializing of state will be useful:
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
return (
<div>
<h1>count: {count} </h1>
<button onClick={handleClick}>button</button>
</div>
);
}
export default App;
🧐Explanation
The reason the count doesn’t increase by 3 with a single click is that the state updates in React are asynchronous. When you call setCount
, React batches the state updates and performs them in a single batch before the next render. This is done for performance optimization.
So, when you call setCount
three times in a row with the same value, React only takes the last value and performs the update. In our case, count
is incremented by 1, but it's done three times sequentially, so the result is an increment of 1.
👉to resolve this we can use updating state based on the previous state
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount((previousValue) => {
return previousValue + 1;
});
setCount((previousValue) => {
return previousValue + 1;
});
setCount((previousValue) => {
return previousValue + 1;
});
};
return (
<div>
<h1>count: {count} </h1>
<button onClick={handleClick}>button</button>
</div>
);
}
export default App;
🧐Explanation
In this updated code, the useState
hook is still used to set up a state variable called count
with an initial value of 0. The setCount
function is used to update the value of count
, but with a slight modification.
Instead of directly updating the state with a new value, the setCount
function is now called with a callback function as an argument. This callback function receives the previous value of the state as a parameter (in this case, previousValue
), and it returns the new value based on the previous value.
In the handleClick
function, setCount
is called three times in a row, each time with the callback function. The callback function takes the previous value and returns the updated value by incrementing it by 1. By using the callback approach, we can ensure that each setCount
call operates on the latest value of count
, avoiding any potential race conditions.
Since each setCount
call is performed separately from the previous value, the count is incremented by 1 for each call. Therefore, when you click the button, the count will increase by 3 because each setCount
call increments the count by 1 individually.
This approach guarantees that the updates are properly batched and executed, even if they happen in rapid succession. By using the callback form of setCount
, you ensure that the state updates are based on the latest value, avoiding potential inconsistencies that may occur with multiple synchronous state updates.
🌟 That’s it! You’re now equipped with the knowledge of updating state based on the previous state in React. Happy coding! 💻✨