Skip to content

useEffect Hook — Side Effects in React

DodaTech 3 min read

In this tutorial, you'll learn about useeffect hook. We cover key concepts, practical examples, and best practices.

What You'll Learn

Master React's useEffect hook — run side effects in function components, fetch API data, subscribe to events, clean up resources, and manage dependencies.

Why It Matters

Side effects are what make apps useful — loading data, updating the DOM, setting up subscriptions. useEffect controls when and how these happen.

Real-World Use

Fetching user data when a profile page loads, updating the document title, subscribing to a WebSocket, or starting/stopping a timer.

Basic useEffect

import { useState, useEffect } from "react";

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    // Cleanup function — runs when component unmounts
    return () => clearInterval(interval);
  }, []);  // Empty array = run once on mount

  return <p>Seconds: {seconds}</p>;
}

Data Fetching

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;

    async function fetchUsers() {
      try {
        setLoading(true);
        setError(null);
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/users"
        );
        if (!response.ok) {
          throw new Error(`HTTP ${response.status}`);
        }
        const data = await response.json();
        if (!cancelled) {
          setUsers(data);
        }
      } catch (err) {
        if (!cancelled) {
          setError(err.message);
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    }

    fetchUsers();

    // Cleanup to avoid state updates on unmounted component
    return () => {
      cancelled = true;
    };
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Dependency Array Patterns

// 1. Runs after EVERY render (rarely used)
useEffect(() => {
  console.log("Rendered!");
});

// 2. Runs ONCE after mount (data fetching, subscriptions)
useEffect(() => {
  fetchData();
}, []);

// 3. Runs when specific values change
useEffect(() => {
  document.title = `User: ${username}`;
}, [username]);

// 4. Runs when any dependency changes
useEffect(() => {
  fetchUserPosts(userId);
}, [userId, page]);

The Dependency Array

If you use a variable inside useEffect, include it in the dependency array:

function ProductPage({ productId }) {
  const [product, setProduct] = useState(null);

  // ❌ Missing dependency
  useEffect(() => {
    fetch(`/api/products/${productId}`)
      .then(r => r.json())
      .then(setProduct);
  }, []);  // Bug: uses productId but doesn't list it

  // ✅ Correct
  useEffect(() => {
    fetch(`/api/products/${productId}`)
      .then(r => r.json())
      .then(setProduct);
  }, [productId]);
}

Cleanup Functions

Cleanup prevents memory leaks and stale effects:

function ChatRoom({ roomId }) {
  useEffect(() => {
    // Setup
    const connection = createConnection(roomId);
    connection.connect();

    // Cleanup — runs when:
    // 1. Component unmounts
    // 2. Before re-running the effect (dependencies changed)
    return () => {
      connection.disconnect();
    };
  }, [roomId]);
}

Event Listeners

function WindowSize() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    function handleResize() {
      setWidth(window.innerWidth);
    }

    window.addEventListener("resize", handleResize);

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

  return <p>Window width: {width}px</p>;
}

Common Mistakes

Mistake Fix
Missing dependency Add all used variables to array
Infinite loop Don't update state that's a dependency
No cleanup Always clean up subscriptions
Async effect function Create async function inside effect
Stale closure Use functional state update
Fetch → setState after unmount Use cancel flag (like cancelled)

Practice

// 1. Fetch data from an API and display it
// 2. Create a timer with start/stop
// 3. Track mouse position with event listener
// 4. Sync document title with a state variable
// 5. Subscribe to a WebSocket and clean up

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro