I am trying to figure out when the re-render occurs when updating state in React with useState hook. In the code below, clicking the button triggers the handleClick function which contains a setTimeout. The callback inside setTimeout is executed after 1 second, which updates the state variable count by calling setCount. A console log then prints a message.
The order I would expect the console logs to show up once the button is clicked are:
- 'Count before update', 0
- 'Count post update in setTimeout', 0
- 'Count in render', 1
However the order I see after running this code is:
- 'Count before update', 0
- 'Count in render', 1
- 'Count post update in setTimeout', 0
How is it that "'Count in render', 1" shows up before "'Count post update in setTimeout', 0"? Doesn't setCount result in the scheduling of a re-render that is not immediate? Shouldn't the console log immediately after the setCount function call always execute before the re-render is triggered?
function AppFunctional() {
const [count, setCount] = React.useState(0);
const handleClick = () => {
console.log('Count before update', count);
setTimeout(() => {
setCount(count + 1);
console.log('Count post update in setTimeout', count);
}, 1000);
};
console.log('Count in render', count);
return (
<div className="container">
<h1>Hello Functional Component!</h1>
<p>Press button to see the magic :)</p>
<button onClick={handleClick}>Increment</button>
{!!count && (
<div className="message">You pressed button {count} times</div>
)}
</div>
);
}
ReactDOM.render(<AppFunctional />, document.querySelector('.react'));
<script crossorigin src="@16/umd/react.development.js"></script>
<script crossorigin src="@16/umd/react-dom.development.js"></script>
<div class='react'></div>
I am trying to figure out when the re-render occurs when updating state in React with useState hook. In the code below, clicking the button triggers the handleClick function which contains a setTimeout. The callback inside setTimeout is executed after 1 second, which updates the state variable count by calling setCount. A console log then prints a message.
The order I would expect the console logs to show up once the button is clicked are:
- 'Count before update', 0
- 'Count post update in setTimeout', 0
- 'Count in render', 1
However the order I see after running this code is:
- 'Count before update', 0
- 'Count in render', 1
- 'Count post update in setTimeout', 0
How is it that "'Count in render', 1" shows up before "'Count post update in setTimeout', 0"? Doesn't setCount result in the scheduling of a re-render that is not immediate? Shouldn't the console log immediately after the setCount function call always execute before the re-render is triggered?
function AppFunctional() {
const [count, setCount] = React.useState(0);
const handleClick = () => {
console.log('Count before update', count);
setTimeout(() => {
setCount(count + 1);
console.log('Count post update in setTimeout', count);
}, 1000);
};
console.log('Count in render', count);
return (
<div className="container">
<h1>Hello Functional Component!</h1>
<p>Press button to see the magic :)</p>
<button onClick={handleClick}>Increment</button>
{!!count && (
<div className="message">You pressed button {count} times</div>
)}
</div>
);
}
ReactDOM.render(<AppFunctional />, document.querySelector('.react'));
<script crossorigin src="https://unpkg./react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg./react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
Share
Improve this question
edited Jan 28, 2022 at 5:15
CertainPerformance
372k55 gold badges352 silver badges357 bronze badges
asked Jan 28, 2022 at 5:12
user18054452user18054452
854 bronze badges
1 Answer
Reset to default 7Doesn't setCount result in the scheduling of a re-render that is not immediate? Shouldn't the console log immediately after the setCount function call always execute before the re-render is triggered?
Under the hood, React optimizes re-renders by queuing and batching them when it can determine that it's safe to do so. When in a function React understands (such as a functional ponent, or a hook callback), if you call a state setter in one of those functions, React will know that it's safe to delay the state update until its processing is finished - for example, until all effect / memo / etc callbacks have run, and until all ponents from the original state have been painted onto the screen.
But when you call a state update outside of a built-in React function, React doesn't know enough about its behavior to know when it'll be able to re-render next if it delays the state update. The setTimeout
call is not called from inside the React lifecycle, so batching updates and optimizing them is much more difficult - so, rather than React trying to guess how it could be done safely and asynchronously, it re-renders immediately.