Chapter 12 of 20 · React for Beginners — Complete Free Tutorial
Effects with useEffect
Every component so far in the React for Beginners series has been a pure function of its props and state: give it the same inputs, get the same UI. But real interfaces must synchronise with things outside React — set the page title, subscribe to a socket, start a timer, focus an input. The useEffect hook is React's tool for exactly that, and this chapter covers its dependency array, cleanup functions, and the discipline that keeps effects predictable.
What counts as an effect
An effect is code that reaches outside your component's render output to touch the browser or a network. Ask one question: does this need to happen because something changed, outside of React's control? If yes, it is an effect.
| Effect (useEffect) | Not an effect |
|---|---|
Set document.title | Compute a total from items (derive during render) |
| Fetch data from an API | Format a price for display |
| Subscribe to a WebSocket | Choose a class name from state |
Add a keydown listener | Update state in response to props (usually) |
The right column is the trap beginners fall into: values you can calculate do not belong in state or effects at all — compute them while rendering.
Your first effect
The hook takes a callback — the effect — that runs after the component commits to the screen, not during render:
import { useEffect, useState } from 'react';
function DocumentTitle({ name }) {
useEffect(() => {
document.title = `${name} — Scriptly`;
}, [name]);
return <h1>{name}</h1>;
}
Expected output: the browser tab reads Ada — Scriptly, and changes to Ravi — Scriptly whenever the name prop changes. The render output itself never mentions the title — that is the division of labour.
The dependency array
The second argument is an array of every value the effect reads. React compares it after each render and re-runs the effect only when something differs:
| Dependencies | When the effect runs | Use for |
|---|---|---|
| Omitted entirely | After every render | Rarely correct — usually a mistake |
[] (empty) | Once, after the first render | Setup that never depends on props/state |
[id] | First render, then whenever id changes | The common case: react to specific changes |
Read [name] as: "this effect uses name, so re-run it when name changes." List every value you read — ESLint's react-hooks plugin will remind you, and honouring it prevents stale-data bugs.
Cleanup: the other half
Many effects reserve a resource that must be released — timers, listeners, subscriptions, in-flight requests. Return a cleanup function; React runs it before the next re-run and on unmount:
function LiveClock() {
const [now, setNow] = useState(() => new Date());
useEffect(() => {
const timer = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(timer); // runs on re-run and unmount
}, []);
return <time>{now.toLocaleTimeString()}</time>;
}
Expected output: the clock ticks once per second; when the component is removed, the interval is cleared — no orphan timer calling setState on an unmounted component. Think of setup and cleanup as a pair written together.
Effect flow, step by step
- Component renders and React commits the UI to the DOM.
- React compares the dependency array with the previous one.
- If it changed (or this is the first render), React runs your effect.
- Before running it again — or when the component unmounts — React runs the cleanup from the previous run.
In development with StrictMode, React deliberately mounts, cleans up and re-runs effects once to prove your cleanup is correct. If your effect is written as a proper pair, this changes nothing visible.
When NOT to use useEffect
- Deriving values — totals, filters and formatted strings are computed during render, not synchronised.
- Responding to user input — update state in the event handler, where the event actually happened.
- Fixing "extra" renders — effects do not make renders fewer; reach for memoisation only after measuring.
Common mistakes
- Missing dependencies — an effect that reads
idbut lists[]keeps showing data for the first id forever. - No cleanup for listeners and timers — duplicates accumulate on every re-run; always pair setup with cleanup.
- Using effects for derivable data — storing a mirrored copy of state invites drift; compute the value instead.
- Calling setState with no guard on fetches — responses can arrive after unmount or out of order; cancel or ignore stale ones (chapter 13).
- Running effects during render — the callback runs after commit; anything synchronous belongs in the component body or an handler.
Practice checklist
- Set the document title from a prop and watch it update when the prop changes.
- Build the live clock; verify the interval stops by logging in the cleanup function.
- Write an effect with
[]and confirm it runs exactly once (twice in StrictMode dev). - Deliberately omit a dependency, observe the stale value, then add it and compare.
- Audit one of your earlier components: replace any stored derived value with a calculation.
Effects make side effects safe — the most common side effect of all is loading remote data, covered next in Fetching Data from an API.
Frequently Asked Questions
React is a JavaScript library for building user interfaces from reusable components. It manages how your UI updates when data changes, so you describe what the screen should look like and React performs the efficient DOM updates. It is one of the most in-demand frontend skills and powers products from Netflix to Instagram, which makes it an excellent next step after core JavaScript.
You should be comfortable with functions, arrays and objects (map, filter, spread), destructuring, modules and basic async/await. React itself is written in plain JavaScript — JSX is a syntax layer on top. If any of those feel shaky, revisit the JavaScript fundamentals first and this series will go much faster.
React is a library focused only on the UI layer — you choose your own router, data layer and tooling. Angular is a full framework that ships routing, forms, HTTP client and testing conventions as one opinionated package. React's flexibility and huge ecosystem make it the common choice for teams that want to compose their own stack.
A component is a reusable function that returns UI. It receives input through props, keeps private data in state, and can render other components. Pages are composed by nesting small components (navbar, card list, footer) instead of one giant file, which keeps code testable and easy to change.
JSX is a syntax extension that looks like HTML but compiles to React's create() calls — it is a description of UI, not markup the browser parses. Small differences matter: class becomes className, style takes an object, and you embed JavaScript expressions inside curly braces. Once compiled, React turns that description into real DOM nodes.
Props are inputs passed from a parent — they are read-only inside the child, like function arguments. State is private, mutable data owned by a component; updating it triggers a re-render. The rule of thumb: values that come from outside the component are props; values that change over the component's lifetime are state.
Rendering means calling your component function to produce a description of the UI for the current state and props. It does not always mean DOM work — React compares the new description with the previous one (reconciliation) and applies only the differences to the real DOM. This is why updating state 100 times does not mean 100 full page rebuilds.
An effect runs after the component has committed to the screen — after render, not during it. You list dependencies in an array, and React re-runs the effect only when one of them changes. If the effect returns a cleanup function, React runs it before the next re-run and on unmount, which is where you cancel timers, abort fetches and remove listeners.
Use Vite. Create React App is effectively in maintenance mode, while Vite starts instantly (native ES modules) and builds fast, and it is the default choice for new React projects. This series uses Vite for every example — you only need Node.js installed to follow along.
React apps compile to static files (HTML, CSS and JavaScript), so any static host works: Netlify, Vercel, GitHub Pages or Cloudflare Pages. Run the production build, upload the dist folder, and configure a single-page-application fallback so unknown paths load index.html — routing chapters explain why that fallback is required.