Lesson 11 of 31
useEffect Basics
What an effect is for — synchronizing a component with something outside React — when it runs relative to rendering and painting, and why it's not just "code that runs after render."
So far, every component has computed its output purely from props and
state — no network calls, no manipulating the DOM by hand, no timers.
useEffect is the hook for the moment a component needs to reach outside
that pure-rendering model, to synchronize with something React doesn't
manage: the browser DOM directly, a timer, a subscription, a server.
Why this needs its own hook
Recall from the reconciliation lesson: rendering is meant to be a pure
description of what the UI should look like, and React may call a
component's function more than once for a single update. If a fetch call
lived directly in the component body, it would fire during rendering itself
— possibly more than once, and even on renders where nothing relevant
changed. useEffect exists specifically to run code after React has
rendered and committed to the real DOM, separately from that pure
description step.
The basic shape
import { useEffect } from "react";
function DocumentTitle({ count }) {
useEffect(() => {
document.title = `${count} clicks`;
});
return <p>{count} clicks so far</p>;
}useEffect takes a function. React calls that function after it renders
and commits — after the DOM actually reflects count clicks — not during
the render itself. With no second argument at all, this effect runs after
every render of this component, which is rarely what you actually want; the
next lesson covers the dependency array, the second argument that controls
exactly when an effect re-runs.
When, precisely, does it run?
- Step 1
Render
Component function runs, producing new elements from current props/state.
- Step 2
Commit
React applies the resulting changes to the real DOM.
- Step 3
Paint
The browser paints the updated pixels — this usually happens before the effect below runs.
- Step 4
Effect runs
React calls the effect function, now that the DOM is up to date and (usually) already visible.
A realistic first example
Synchronizing the document title with component state is a small, honest example of "something outside React" — the browser tab title isn't part of the DOM tree React manages, so it can't just be returned as JSX:
import { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}Every time count changes, the component re-renders, the DOM updates, and
then this effect runs and updates the title to match — state and an
external system (the tab title) staying in sync.
What belongs in an effect, roughly
A reasonable rule of thumb: if the code needs to happen once React has
actually committed something to the page — because it touches the real DOM,
starts a subscription, sets a timer, or calls out to a network — it belongs
in an effect. If it's just deriving a value from props or state for display,
it doesn't need an effect at all — it's a plain calculation in the render
itself (or, once you learn it in a later lesson, a useMemo if that
calculation is genuinely expensive).
Try it yourself
What to remember
- useEffect synchronizes a component with something outside React's rendering — the DOM, timers, subscriptions, network calls.
- An effect runs after React commits to the real DOM, never during rendering itself.
- With no dependency array, an effect runs after every render — the next lesson covers controlling that precisely.
- useLayoutEffect is a rare, synchronous alternative for the narrow case of avoiding a visible flicker; useEffect is the correct default.
Check yourself
4 questions · pass 3/4 to unlock The Dependency Array
1.What is useEffect fundamentally for?
2.Relative to rendering, when does an effect with no dependency array run?
3.Given
useEffect(() => { document.title = \${count} clicks\; });with no dependency array, when does this effect re-run?4.Why shouldn't a fetch call go directly in the body of a component function, outside of an effect?
4 left to answer