Im reading the learning react documentation on the react website and in the Queueing A Series of State Updates section there is a sentence that reads
"the UI won’t be updated until after your event handles, and any code in it, completes." From this I get the impression that there can only be one render
per onClick event but in running the following component it seems the component re-renders twice. Once when adding to the pending state and another when decrementing the pending state and incrementing state inside the setTimeout function.
function RequestTracker() {
const [pending, setPending] = useState(0);
const [completed, setCompleted] = useState(0);
useEffect(() => {
console.log("MyComponent rendered!");
});
function handleClick() {
setPending((p) => p + 1);
setTimeout(() => {
setCompleted((c) => c + 1);
setPending((p) => p - 1);
}, 3000);
}
return (
<>
<h3>Pending: {pending}</h3>
<h3>Completed: {completed}</h3>
<button onClick={handleClick}>Buy</button>
</>
);
}
Doesn’t this contradict the statement that React only renders the UI after the event handler completes all code in it?
Using the useEffect there is indeed more than one render per click. How should I think about
how React queues and executes re-renders. Thank you!
>Solution :
the UI won’t be updated until after your event handles, and any code in it, completes.
This statement is true. Your handleClick function is completing before the component re-renders.
but in running the following component it seems the component re-renders twice
That’s to be expected in this case. Look closely at what the code is doing:
function handleClick() {
setPending((p) => p + 1);
setTimeout(() => {
setCompleted((c) => c + 1);
setPending((p) => p - 1);
}, 3000);
}
What does the function handleClick do? Exactly two things:
- Call
setPendingto queue a state update. - Call
setTimeoutto schedule some function to execute at some later time.
Then it’s done. The function has run to completion, performed all of its tasks, and the framework now sees that a state update has been queued and the framework re-renders the component with the new state.
Then, at some later time, this happens:
setCompleted((c) => c + 1);
setPending((p) => p - 1);
Which does exactly two things:
- Call
setCompletedto queue a state update. - Call
setPendingto queue a state update.
Once this is done, this operation has completed its logic. The React framework then sees that there are state updates, processes them, and re-renders the component accordingly.
Overall it seems that your confusion is from an expectation that setTimeout is a blocking operation. It isn’t. It schedules logic to happen at some later time, and then frees up the JavaScript engine to continue processing.