ADVANCED REACTJS • LESSON 28 OF 36

Effects, Cleanup and Race Conditions

Keep obsolete asynchronous work from replacing current results.

Read the explanation, run the worked code, then complete the practice lab in Assignments. This advanced lesson includes worked code and a dedicated practice lab.

Understand the idea

An effect synchronizes a committed UI with something outside React, such as a subscription, timer or request. Its dependency list describes the reactive values used by that synchronization. Cleanup ends the old synchronization before the next setup or unmount. Strict Mode tests this lifecycle again in development.

A search can finish out of order: an earlier request might be slower than a later one. Without protection, its response can overwrite the result for the currently selected query. Cancellation reduces unnecessary work, and an active flag can reject results from an obsolete effect. The example uses controlled delays and cancellation so the problem is visible without relying on a live API.

Before adding an effect, ask whether the work is a calculation or an event response. Filtering an already-loaded list belongs in render. Saving because the user clicked Submit belongs in that handler. An effect should not mirror one state variable into another when the value can simply be derived. For real fetch calls, also check response.ok and catch network or parsing errors; AbortController can cancel an obsolete request.

Dependency changes ↓ Clean up previous work ↓ Start current synchronization ↓ Accept only current results
Trace the values through each step before changing the code.

Run the example

Use a separate React practice project with a Node.js version supported by Vite. Create it once with these commands, then use its development URL. Do not paste this into the Edupoly website’s own source files.

npm create vite@latest react-practice -- --template react
cd react-practice
npm install
npm run dev

Replace src/App.jsx with the following complete example. Keep Vite’s generated src/main.jsx entry point.

src/App.jsx

import { useEffect, useState } from 'react';
export default function App() {
  const [student, setStudent] = useState('Asha');
  const [result, setResult] = useState(null);
  useEffect(() => {
    let active = true;
    const timer = setTimeout(() => {
      if (active) setResult({student, text: `${student}'s report`});
    }, student === 'Asha' ? 1500 : 200);
    return () => {active = false; clearTimeout(timer);};
  }, [student]);
  return <main><label>Student <select value={student} onChange={e => setStudent(e.target.value)}><option>Asha</option><option>Ravi</option></select></label>
    <p role="status">{result?.student === student ? result.text : `Loading ${student}…`}</p>
  </main>;
}

Expected result

Select Ravi before Asha’s 1.5-second delay ends. Ravi’s report appears and is not replaced by Asha’s old result.

Apply this to your assignments

Switch repeatedly between students. Temporarily remove both cleanup protections and reproduce the stale-result problem, then restore them. Adapt the safe pattern to your Countries API example and test slow responses, failed status codes and unmounting.

Practice starting point

Use the worked example above as your starting point.

Open Assignments for this lesson’s practice lab and acceptance checks.

Common mistakes

Do not hide missing dependencies to force an effect to run once. Cleanup must undo the resource created by that setup. A cancelled request should not produce a user-facing failure message.

Check your understanding

  1. Predict the output before running the example, then compare it with the result panel.
  2. Explain which value each expression reads and which action changes it.
  3. Try the boundary cases listed in the practice task and record what you observe.
  4. Rebuild one matching assignment without copying, then compare behavior with the original example.

Reference

Official documentation for this topic · Vite setup requirements