Command Palette

Search for a command to run...

Hectal
PHASE 7Intermediate React 16.8+ ~23 min· topic 1 of 9

Topic 7.1

Effects: Syncing with the Outside World

In one line

An effect is code that keeps a component in sync with something outside React (a timer, a connection, the page title, a browser API); you write it with useEffect, and React runs it after the component's changes are committed to the screen.

Think of it like this

A shop sign that says OPEN. The shopkeeper doesn't flip the sign while deciding what to sell (that's thinking), and not only when a customer walks in (that's an event). The rule is "whenever the shop is open, the sign says OPEN". When the shop closes, the sign is turned back. An effect is that rule: while this component is on the screen, keep something outside React matching it, and undo it when it leaves.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Side effect
Anything code does besides returning its result: changing outside data, starting a timer, writing the page title, sending a request.
Pure rendering
A component only calculates its JSX from props and state, with no side effects (Topic 2.6).
Event handler
A function that runs because the user did something, like onClick. The usual home for side effects.
Effect
Code that runs because a component rendered, to keep something outside React in sync with it.
useEffect
The hook that declares an effect: useEffect(setup, dependencies?).
External system
Anything React doesn't control: the DOM title, timers, network connections, browser APIs, other libraries.
Commit
The step where React applies the changes to the real DOM, after rendering.
Paint
The browser drawing the updated page as pixels on the screen.
Cleanup
The function an effect returns. It undoes what the effect set up.
Escape hatch
A feature for stepping outside React's normal model when you must talk to other systems.

Step by step

01Three kinds of code in a component

Rendering code at the top level of the component calculates JSX. It must be pure. Event handlers run when the user does something. Effects run after rendering, to sync with the outside world.

ChatRoom.jsxwhole filejsx
function ChatRoom({ roomId }) {
  // 1. Rendering: pure calculation only
  const title = "Welcome to #" + roomId;

  // 2. Effect: runs after the screen updates
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);

  // 3. Event handler: runs when the user clicks
  function handleSend() {
    sendMessage(roomId, "Hi!");
  }

  return <h1 onClick={handleSend}>{title}</h1>;
}

02Ask: what caused this side effect?

If a specific interaction caused it ("the user clicked Buy"), put it in that event's handler. If being on screen causes it ("while this is visible, stay connected"), it's an effect. This one question sorts almost every case.

Ask: what caused this side effect?diagram
Rendering diagram…

03Declare the effect

Import useEffect and call it at the top level of the component, never inside a condition or loop. Pass a function. React stores it and runs it later, after the commit.

Inbox.jsxwhole filejsx
import { useEffect } from "react";

function Inbox({ unread }) {
  useEffect(() => {
    document.title = "(" + unread + ") Inbox";
  });
  return <p>{unread} unread</p>;
}

04When React runs it

Every update goes through the same steps. Your component renders, React commits the DOM changes, the browser paints, and only then does the effect run. So an effect can read the DOM safely: it already contains the new output.

When React runs itdiagram
Rendering diagram…

05Return a cleanup when you set something up

If the effect starts something that keeps going (a connection, a timer, a listener), return a function that stops it. React calls it before the next run and when the component leaves the screen. Topic 7.3 is all about cleanup.

Try it yourself

  1. 1

    Move the title code into render

    In the inbox example, cut document.title = ... out of the effect and paste it straight into the component body. Predict: does the title still update? (It does here, but now it runs during render, which breaks purity: StrictMode would run it twice, and a render React throws away would still change the title.)

    Put it back into the effect.

  2. 2

    Count the renders

    In the first example, change the scripted click into two clicks 30 ms apart. Predict the log lines before you run: each click gives one render line followed by one effect line.

  3. 3

    Type in the chat input

    In the chat example, type in the input. You see render lines, but no new connected line: the effect's empty array tells React it never needs to re-sync.

Code & diagrams

Render, commit, then the effect New tab

`root.render()` only schedules the first render, so its log comes first. Each render is followed by the effect, and by then the DOM already holds the new text. The click is scripted after 100 ms.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

root.render() returned
render: count = 0
effect: the page already shows "Count: 0"
render: count = 1
effect: the page already shows "Count: 1"
Sync the tab title with state New tab

The tab title is outside React: JSX can't render it. The effect copies state into it after every render, so the title can never fall behind the screen.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

title is now: (3) Inbox
title is now: (2) Inbox
title is now: (1) Inbox
title is now: Inbox
Rendering, events and effects side by side New tab

Typing re-renders the component, but the `[]` dependency array means the connection is not redone. Sending happens only on click. Each kind of code has its own home.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

render: building the JSX
effect: connected to #general
event: sent "hello"
Where each kind of code runsdiagram
Rendering diagram…

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Call a state setter in an effect with no dependency array

In the inbox example, add setUnread(unread + 1); inside the effect.

terminal
$ Console
── what you'll see ──
Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
(the same error repeats, and the number on the page keeps climbing)

Myth vs fact

Myth

useEffect is how you run code "on mount", like componentDidMount.

Fact

It runs after a render to sync with an outside system. Thinking "on mount" leads to missing updates when props change. Topic 7.5 shows the synchronising mindset.

Myth

Effects run before the screen updates.

Fact

Effects run after React commits to the DOM, and usually after the browser paints. Code that must run before paint uses useLayoutEffect (Topic 7.8).

Myth

Anything with a side effect belongs in useEffect.

Fact

Side effects caused by a user action belong in the event handler. Effects are for syncing caused by being on screen.

Myth

Effects run on the server too.

Fact

Server rendering only produces HTML. Effects run in the browser after hydration (Phase 20).

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Internally, useEffect marks the component's fiber with a Passive effect flag. After the commit, React schedules a separate task to flush passive effects. For updates from discrete events (click, keydown), React 18+ flushes them synchronously at the end of the commit so the result is consistent before the next input, which is why useEffect is "usually" but not always after paint.

  • ▸

    Effects run child-first: in a tree, children's effects run before their parents', because a child's commit finishes first. Cleanups also run before any new setups in the same commit.

  • ▸

    The setup function must return either nothing or a cleanup function. An async function returns a Promise, so useEffect(async () => ...) is a mistake; React warns in development. Define an async function inside the effect and call it (Topic 7.7).

  • ▸

    Effect code runs in the browser only, so it's the safe place for window, localStorage and other browser-only APIs in apps that are also rendered on a server.

Remember this

  1. 1

    A side effect is anything a piece of code does besides calculating its result: changing a variable outside itself, writing to the page title, starting a timer, sending a network request, logging. In Topic 2.6 you learned that rendering must be pure: a component only calculates JSX from its props and state, with no side effects. So where does the other code go? React gives it two homes.

  2. 2

    Home one is event handlers: functions like onClick that run because the user did something specific. Sending a message, adding to the cart, submitting a form: these happen *because of an interaction*, so they belong in handlers. Handlers may have side effects freely. Most side effects in an app belong here.

  3. 3

    Home two is effects: side effects caused by *rendering itself*, not by a particular interaction. A chat room should be connected whenever it is on screen, whether you arrived by clicking a link, pressing Back or reloading. No single event "causes" it. You declare one with the useEffect hook: useEffect(() => { ...setup... return () => { ...cleanup... }; }, [dependencies]). A hook is a function starting with use that you call at the top level of a component (Topic 4.2).

  4. 4

    Timing is the key fact. Rendering builds the JSX; then React commits (updates the real DOM); then the browser usually paints (draws pixels); and *then* React runs your effects. So inside an effect the DOM already shows the new values, and the user may already see them. Effects never block the screen from updating. One detail for seniors: when an update comes from a discrete user input like a click, React 18+ runs that update's effects before the browser paints, but you should never rely on paint timing in useEffect (Topic 7.8 has the hook for that).

  5. 5

    By default an effect runs after every render (the first one and each re-render). The second argument, the dependency array, tells React to skip it when certain values haven't changed (Topic 7.2). The function you return is the cleanup, which undoes the setup: React runs it before the effect runs again and when the component is removed (Topic 7.3).

  6. 6

    Effects are an escape hatch: a way to step outside React to talk to systems React doesn't manage (browser APIs, third-party widgets, network connections). If no outside system is involved, you probably don't need an effect at all (Topic 7.6). Effects also don't run during server rendering (Phase 20), only in the browser.

Explain it without notes

01

What is an effect in React, and how is it different from an event handler?

02

Describe exactly when a useEffect callback runs relative to rendering, the DOM commit and the browser paint.

03

Why can't side effects go straight into the component body?

04

Give three examples of external systems you'd sync with an effect, and one side effect that should not be an effect.

Practice

01

Write a component Clock that sets document.title to "Hello, " + name using a name prop.

02

In the chat example, which log lines appear if the component re-renders 5 times because of typing? Why?

Trade-offs

  • ↔

    Effects keep outside systems in sync automatically, but every effect is one more thing that runs after renders. Unneeded effects cause extra renders and timing bugs; prefer calculating during render or handling events.

  • ↔

    Running after paint keeps the screen responsive, but means the user can briefly see the state before the effect's work; for visual measurements that matters (Topic 7.8).

Done when you can

  • I can explain what a side effect is and why render must be pure.

  • I can decide whether code belongs in render, an event handler or an effect.

  • I can write a useEffect that syncs something outside React.

  • I can describe the order: render, commit, paint, effect.

  • I know effects don't run on the server.