Simply put, the useEffect
hook runs at the end of the render cycle after content has been flushed to the DOM. Each useEffect
hook runs each render cycle in the order they are declared. Each useEffect
hook enqueues a state update on a different render cycle based on the dependency's value in the effect callback.
But are the intermediate statuses "one" and "two" always printed on
the DOM, even for a blinky fraction of a second?
Yes, they are. Each state update is enqueued in a different render cycle, so each update will be processed and the reconciled diff flushed to the DOM and rendered. The React framework aims for about a 60fps refresh rate, or, about 17ms per render cycle. The basic math: 1s/60render * 1000ms/1s = 1000ms/60render = 16.6667ms/render. 17ms is nearly imperceptible I dare say.
If you take this into account and multiply by the 3 triggered state updates, we're talking about around 50ms for just React doing the work. That's still nearly imperceptible. For reference, it seems the average time for a human to blink is between 100-150ms.
You can also very easily test this out for yourself with a couple extra useEffect
hooks logging out the state updates and render cycles. Take note of the timestamps in the log output.
function App() {
const [status, setStatus] = React.useState("zero");
const handleChangeStatus = () => {
setStatus("one");
};
React.useEffect(() => {
if (status === "one") {
setStatus("two");
}
}, [status]);
React.useEffect(() => {
if (status === "two") {
setStatus("three");
}
}, [status]);
React.useEffect(() => {
console.log("Status updated", status);
}, [status]);
React.useEffect(() => {
console.log("Component rendered")
})
return (
<div>
<div id={status}>{status}</div>
<button onClick={handleChangeStatus}>Change statuses</button>
</div>
);
}
const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root" />