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.
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.
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.
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.
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
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
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
renderline followed by oneeffectline. - 3
Type in the chat input
In the chat example, type in the input. You see
renderlines, but no newconnectedline: the effect's empty array tells React it never needs to re-sync.
Code & diagrams
`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
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"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
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: InboxTyping 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
Console
Nothing logged yet.Expected console output
render: building the JSX
effect: connected to #general
event: sent "hello"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.
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,
useEffectmarks 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 whyuseEffectis "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
asyncfunction returns a Promise, souseEffect(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,localStorageand other browser-only APIs in apps that are also rendered on a server.
Remember this
- 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
Home one is event handlers: functions like
onClickthat 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
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
useEffecthook:useEffect(() => { ...setup... return () => { ...cleanup... }; }, [dependencies]). A hook is a function starting withusethat you call at the top level of a component (Topic 4.2). - 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
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
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
What is an effect in React, and how is it different from an event handler?
Describe exactly when a useEffect callback runs relative to rendering, the DOM commit and the browser paint.
Why can't side effects go straight into the component body?
Give three examples of external systems you'd sync with an effect, and one side effect that should not be an effect.
Practice
Write a component Clock that sets document.title to "Hello, " + name using a name prop.
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
useEffectthat syncs something outside React.I can describe the order: render, commit, paint, effect.
I know effects don't run on the server.