Back to List

useEffect β€” Side Effects and Lifecycle

This explains the execution timing of useEffect, the role of the dependency array, the cleanup function, and common mistakes.

Intermediate
|
5min
|
Verified (2026-07)
Progress0/55 (0%)

useEffect β€” Side Effects and Lifecycle

After completing this topic, you will:

Understand when useEffect runs, what a dependency array is, and why cleanup functions are necessary.


What are Side Effects?

The main role of a React component is to receive state and render UI. However, other tasks are also required:

  • Fetching data from an API
  • Setting timers
  • Modifying the document title
  • Registering event listeners

These tasks are called side effects. useEffect is a Hook that handles these side effects.


Basic Usage

javascript
import { useEffect, useState } from "react";

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

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

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

useEffect takes two arguments:

  1. A function to execute (the effect)
  2. A dependency array (when to re-run)

Dependency Array

The execution time depends on the dependency array:

javascript
// Runs on every render (no dependency array)
useEffect(() => {
  console.log("Runs on every render");
});

// Runs only once on mount (empty array)
useEffect(() => {
  console.log("Runs only once");
}, []);

// Runs whenever `userId` changes
useEffect(() => {
  fetchUser(userId);
}, [userId]);

React compares the values in the dependency array with the previous render. If the values are different, the effect is re-executed.


Cleanup Function

javascript
useEffect(() => {
  const timer = setInterval(() => {
    console.log("tick");
  }, 1000);

  return () => {
    clearInterval(timer);
  };
}, []);

If the effect function returns a function, that is the cleanup function. It is executed in the following situations:

  • When the component unmounts (disappears from the screen)
  • Just before the effect is re-executed (when the dependency changes)

It is used for cleanup tasks such as clearing timers, removing event listeners, and unsubscribing.

javascript
useEffect(() => {
  const handleResize = () => setWidth(window.innerWidth);
  window.addEventListener("resize", handleResize);

  return () => window.removeEventListener("resize", handleResize);
}, []);

Common Mistake: Infinite Loop

javascript
// Infinite loop!
useEffect(() => {
  setCount(count + 1);
}); // No dependency array β†’ runs on every render β†’ setState β†’ re-render β†’ runs again

If you modify state within the effect and there is no dependency array, the effect will run again after each re-render.

javascript
// Correct way
useEffect(() => {
  fetchData().then(setData);
}, []); // Empty array β†’ runs only once

Key Takeaways

useEffect executes side effects after rendering. The dependency array, if empty, runs only once on mount; if populated, it runs when the values change. Use the cleanup function to clean up timers, listeners, etc., to prevent memory leaks.

πŸ’¬ Questions & Comments

0 comments

You can post without signing in. Guest comments cannot be edited or deleted by their author.

0/2000

Loading...