From React DOCS:
https://reactjs.org/docs/state-and-lifecycle.html
State Updates May Be Asynchronous
React may batch multiple setState() calls into a single update for performance.
This makes total sense. If you have something like the function below, it would be very inefficient to re-render on every setState
call
const [state1,setState1] = useState(false);
const [state2,setState2] = useState(false);
const [state3,setState3] = useState(false);
function handleClick() {
setState1(true);
setState2(true);
setState3(true);
}
So, in the situation above, I expect React to batch all 3 setState
calls into a single re-render. And it does exactly that!
But what I want to know is:
Once handleClick
has completed, is a re-render guaranteed to happen immediatelly handleClick
has done running? I mean literally immediately, like synchronously immediately?
From this snippet that I've build, it seems that this is true. React will synchronously apply the updates (re-render) after handleClick
has completed. Correct me if I'm wrong in assuming that.
See snippet below:
- Click 3 times as fast as you can
handleClick
will call asetState
and will log the currentprops.counter
- There is an expensive loop on
App
, so it will take a long time to re-render - You'll be able to click much faster than React can re-render the whole App
- But you'll see the
props.counter
is different every time, without any repetition, even if you click multiple times really fast - It means that once your 2nd click is processed (it will take a while because of the expensive loop), React has already re-rendered the whole thing and the
handleClick
function has already been recreated with the new value forprops.counter
that came from thecounter
updated state. - Try clicking 5 times really fast and you'll see that the behavior is the same.
QUESTION
When setState
calls are made inside an event handler function, once that handler function has completed running, is it guaranteed that a re-render will occur immediately (synchronously) after that completion of the handler?
function App() {
console.log("App rendering...");
const [counter, setCounter] = React.useState(0);
// AN EXPENSIVE LOOP TO SLOW DOWN THE RENDER OF 'App'
for (let i = 0; i < 100000; ) {
for (let j = 0; j < 10000; j++) {}
i = i + 1;
}
return <Child counter={counter} setCounter={setCounter} />;
}
function Child(props) {
console.log("Child rendering...");
// THIS FUNCTION WILL CALL 'setState'
// AND WILL ALSO LOG THE CURRENT 'props.counter'
function handleClick() {
props.setCounter(prevState => prevState + 1);
console.log(props.counter);
}
return (
<React.Fragment>
<div>Counter: {props.counter}</div>
<button onClick={handleClick}>Click</button>
</React.Fragment>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>