AniUI Academy

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."

9 min read

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?

  1. Step 1

    Render

    Component function runs, producing new elements from current props/state.

  2. Step 2

    Commit

    React applies the resulting changes to the real DOM.

  3. Step 3

    Paint

    The browser paints the updated pixels — this usually happens before the effect below runs.

  4. Step 4

    Effect runs

    React calls the effect function, now that the DOM is up to date and (usually) already visible.

An effect is scheduled after commit, and in the common case after the browser has painted — it never runs during the render itself.

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

Try it yourself
Loading playground...

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

up to 50
  1. 1.What is useEffect fundamentally for?

  2. 2.Relative to rendering, when does an effect with no dependency array run?

  3. 3.Given useEffect(() => { document.title = \${count} clicks\; }); with no dependency array, when does this effect re-run?

  4. 4.Why shouldn't a fetch call go directly in the body of a component function, outside of an effect?

4 left to answer