पाठ 11 / 25

useEffect, Dependencies and Cleanup

Setup, cleanup, re-run when dependencies change.

Sync with external systems

useEffect(setup, deps) runs setup after the component renders, to synchronise with something outside React: a subscription, a timer, a browser API, a non-React widget. If setup returns a function, React calls it as cleanup before re-running the effect and when the component unmounts. The dependency array lists values the effect uses; React re-runs the effect only when one of them changes. In development, Strict Mode runs setup and cleanup an extra time to reveal missing cleanup.

Synchronise with the outside world

Effects run after rendering to sync with systems outside React, with cleanup.

Three ideas: lifecycle and cleanup, data fetching, when not to use effects.
Figure 4.1 — Lifecycle, fetching and avoiding effects.

Effect lifecycle with changing dependencies, run

I ran this with React 19.3 and react-dom 19.3 on Node.js 22, compiling JSX with esbuild 0.28. Browser-style examples use jsdom 29 as a fake DOM (imported from a small setup-dom.mjs file) and act() to apply updates. Rendering with zone IST subscribes once; re-rendering with the same zone does nothing; switching to UTC cleans up IST then subscribes UTC; unmounting cleans up UTC.

import "./setup-dom.mjs";   // test setup: jsdom provides a browser-like DOM in Node
import { useState, useEffect, act } from "react";
import { createRoot } from "react-dom/client";

const log = [];
function Clock({ zone }) {
  useEffect(() => {
    log.push(`subscribe ${zone}`);
    return () => log.push(`unsubscribe ${zone}`);   // cleanup
  }, [zone]);
  return <p>{zone}</p>;
}
function App({ zone, show }) { return show ? <Clock zone={zone} /> : <p>hidden</p>; }
const root = createRoot(document.getElementById("root"));
await act(() => root.render(<App zone="IST" show />));
await act(() => root.render(<App zone="IST" show />));   // same dependency: effect does not re-run
await act(() => root.render(<App zone="UTC" show />));   // dependency changed
await act(() => root.render(<App zone="UTC" show={false} />));   // unmount
console.log(log.join("\n"));

Output:

subscribe IST
unsubscribe IST
subscribe UTC
unsubscribe UTC

Every subscription needs cleanup

If setup adds a listener, timer or connection, the cleanup must remove it, or you will leak and duplicate them.

त्वरित जाँच: When does React run an effect's cleanup function?

  • Before the first render
  • Only on page reload
  • Never
  • Before re-running the effect and when the component unmounts
Answer

Before re-running the effect and when the component unmounts — Cleanup undoes setup.