Side Effects with useEffect

useEffect runs code after render to sync with things outside React.

SyntaxuseEffect(() => { // effect return () => { /* cleanup */ }; }, [deps]);

useEffect lets you run side effects — things outside React's rendering, like subscriptions, timers, or manually touching the DOM — after the component renders.

Dependencies & cleanup

  • The dependency array controls when the effect re-runs: [] runs once on mount; [a, b] re-runs when a or b change.
  • Return a cleanup function to undo the effect (clear a timer, remove a listener). React runs it before the next effect and on unmount.
useEffect lifecycle: mount runs the effect, dependency changes run cleanup then the effect again, and unmount runs the final cleanupMountrun effectDepschangecleanup +re-runUnmountcleanuprepeats while deps change
The cleanup runs before every re-run and once more when the component unmounts.

Example

Example · javascript
import { useState, useEffect } from 'react';

function Clock() {
  const [now, setNow] = useState(new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id); // cleanup
  }, []);
  return <p>{now.toLocaleTimeString()}</p>;
}

When to use it

  • A user profile page fetches account data with useEffect after mounting and cancels the in-flight request with an AbortController if the user navigates away before it completes.
  • A chat component subscribes to a WebSocket inside useEffect and returns a cleanup function that closes the connection when the component unmounts.
  • A document-title manager uses useEffect with a dependency on the page name so the browser tab title always reflects the currently viewed section.

More examples

Fetch data on mount

Fetches data after render, re-runs when userId changes, and cleans up with AbortController on unmount or re-run.

Example · jsx
import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    fetch(`/api/users/${userId}`, { signal: controller.signal })
      .then(r => r.json())
      .then(setUser)
      .catch(() => {});
    return () => controller.abort();
  }, [userId]);

  if (!user) return <p>Loading...</p>;
  return <h2>{user.name}</h2>;
}

Sync document title

Updates the browser tab title whenever the title prop changes and restores the previous title on cleanup.

Example · jsx
import { useEffect } from 'react';

function PageTitle({ title }) {
  useEffect(() => {
    const previous = document.title;
    document.title = `${title} | MyApp`;
    return () => { document.title = previous; };
  }, [title]);

  return null;
}

Subscribe and clean up

Subscribes to browser network events in useEffect and removes them in the cleanup to prevent memory leaks.

Example · jsx
import { useState, useEffect } from 'react';

function OnlineStatus() {
  const [online, setOnline] = useState(navigator.onLine);

  useEffect(() => {
    const handleOnline = () => setOnline(true);
    const handleOffline = () => setOnline(false);
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return <p>{online ? 'Online' : 'Offline'}</p>;
}

Discussion

  • Be the first to comment on this lesson.