The Codex / Technologies / React / useEffect & Lifecycle

useEffect & Lifecycle

Synchronizing components with external systems - data fetching, subscriptions, timers, and the cleanup function explained properly.

What it is, in plain English: useEffect lets a component synchronize with something outside React's control -- fetching data, setting up a subscription, manually changing the DOM, starting a timer. It runs after the component renders. The dependency array controls when it re-runs: empty array means once on mount, no array means every render, a list of values means whenever those values change. The optional cleanup function (returned from the effect) runs before the next effect and when the component unmounts -- essential for preventing leaks.

Doing things outside of rendering

So far every example just returns JSX based on props and state -- pure rendering. But real apps need to do things React doesn't handle automatically: fetch data from a server, set up a timer, subscribe to an event. These are called side effects, and useEffect is how you handle them.

The basic pattern

React.useEffect(() => {
  // code that runs AFTER the component renders
  console.log("Component rendered!");
});

// Most of the time, you'll provide a dependency array as the second argument:
React.useEffect(() => {
  console.log("This runs once, after the first render");
}, []);  // empty array = run once, like componentDidMount in old class components

The dependency array controls WHEN it runs

// No array -- runs after EVERY render (rarely what you want):
React.useEffect(() => {
  console.log("Runs after every single render");
});

// Empty array -- runs ONCE, after the first render only:
React.useEffect(() => {
  console.log("Runs once, on mount");
}, []);

// Array with values -- runs after first render AND whenever those values change:
React.useEffect(() => {
  console.log(`userId changed to ${userId}`);
}, [userId]);

function Profile({ userId }) {
  React.useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);  // re-fetch whenever userId changes
}

Fetching data -- the most common use case

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

  React.useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]);  // re-fetch when userId changes

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

The cleanup function

// If your effect sets something up, it often needs to tear it down.
// Return a function from the effect -- React calls it before the next
// effect run, and when the component unmounts:
function Timer() {
  React.useEffect(() => {
    const id = setInterval(() => console.log("tick"), 1000);

    return () => {
      clearInterval(id);   // cleanup -- stop the timer
    };
  }, []);
}

// Without cleanup, the timer keeps running even after the component
// is removed from the page -- a memory leak.
Try It Yourself (JSX)

Official Sources

The Codex links only to official documentation. No third-party tutorials.