CodingNeed.

3 · Connect, test and build · 18 MIN

Use an effect only for an external system

Effects synchronize with things outside React, and may need cleanup.

The clock is driven by a browser timer rather than a click. useEffect creates that timer after a render is committed, then returns a cleanup function that cancels it. The empty dependency array means the effect does not depend on changing props or state; the functional setter reads the latest count. Development Strict Mode may start, clean up and restart the effect to expose missing cleanup. Filtering an array does not need this mechanism: calculate derived values while rendering.

Identify setInterval as the external browser API. Observe the returned function runs when the component leaves. Toggle the child and distinguish hiding it from merely hiding CSS. Explain the extra development setup/cleanup cycle.

Before you start

Install Node.js 22.12+ (or a supported newer LTS). In a terminal run: npm create vite@latest codingneed-react -- --template react. Then cd codingneed-react, npm install, and npm run dev. Open the local URL printed by Vite. Replace src/App.jsx with ONE lesson example at a time. JSX renders a browser interface; it is not a plain console script.

File for this example: App.jsx

New words, explained

effect
Synchronization after React commits a render.
cleanup
Undoing a subscription, timer or other resource.
unmount
Removing a component and its associated state.

Follow the example step by step

  1. Identify setInterval as the external browser API.
  2. Observe the returned function runs when the component leaves.
  3. Toggle the child and distinguish hiding it from merely hiding CSS.
  4. Explain the extra development setup/cleanup cycle.

You are ready to move on when: Hidden timers are cleaned up. Showing a timer resets its component state. Explain why this needs an effect but list filtering does not.

Read the example

import {useEffect, useState} from 'react';
function Timer() {
  const [seconds, setSeconds] = useState(0);
  useEffect(() => {
    const timer = setInterval(() => setSeconds(value => value + 1), 1000);
    return () => clearInterval(timer);
  }, []);
  return <p>Study time: {seconds} seconds</p>;
}
export default function App() {
  const [show, setShow] = useState(true);
  return <main><h1>Study timer</h1><button onClick={() => setShow(value => !value)}>Toggle timer</button>{show && <Timer />}</main>;
}
Check the expected output
The timer increments about once a second. Hiding it cancels its interval. Showing it mounts a fresh timer at zero.

Your challenge

Add a console message in setup and cleanup to observe their order. Toggle the timer five times and confirm it does not tick faster.

Solution cost: Discuss the operations in this small example; rendering and I/O costs depend on the host. time · Proportional to the example’s retained data. space

Common trap

An empty dependency array is not permission to ignore values that the effect reads.

Study the project implementation
import {useEffect, useState} from 'react';
function Timer() {
  const [seconds, setSeconds] = useState(0);
  useEffect(() => {
    const timer = setInterval(() => setSeconds(value => value + 1), 1000);
    return () => clearInterval(timer);
  }, []);
  return <p>Study time: {seconds} seconds</p>;
}
export default function App() {
  const [show, setShow] = useState(true);
  return <main><h1>Study timer</h1><button onClick={() => setShow(value => !value)}>Toggle timer</button>{show && <Timer />}</main>;
}

Further reading: Official documentation

Next lesson: Project · Build a filterable reading list

Essential cookies keep your account signed in. Optional analytics is not configured on this site. Your choice does not affect access to lessons.

Read the Privacy Policy. You can change this choice in the footer.