Skillversitas

useEffect Hook

What Are Side Effects?

Side effects are operations that affect the outside world or depend on it. Examples: fetching data, subscriptions, timers, DOM manipulation, and logging.

useEffect Basics

import React, { useState, useEffect } from 'react';

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

  useEffect(() => {
    document.title = `Time: ${seconds} seconds`;
  });

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

Dependency Array

useEffect accepts a dependency array as its second parameter.

No Array (Every Render)

// Runs every time the component renders
useEffect(() => {
  console.log('Render');
});

Empty Array (Mount Only)

// Runs once when the component mounts
useEffect(() => {
  console.log('Component mounted');
}, []);

With Dependencies

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

Cleanup Function

Return a function from useEffect for cleanup.

function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = connectToRoom(roomId);

    return () => {
      connection.disconnect(); // Cleanup
    };
  }, [roomId]);

  return <p>Connected to {roomId}</p>;
}

Example: Fetching Data from an API

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading(true);
    fetch(`/api/users/${userId}`)
      .then(res => {
        if (!res.ok) throw new Error('Failed to load');
        return res.json();
      })
      .then(data => {
        setUser(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, [userId]);

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

Timers and Intervals

function Stopwatch() {
  const [time, setTime] = useState(0);
  const [running, setRunning] = useState(false);

  useEffect(() => {
    if (!running) return;

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

    return () => clearInterval(interval);
  }, [running]);

  return (
    <div>
      <p>{time} seconds</p>
      <button onClick={() => setRunning(!running)}>
        {running ? 'Stop' : 'Start'}
      </button>
    </div>
  );
}

Event Listeners

function WindowSize() {
  const [size, setSize] = useState({ width: 0, height: 0 });

  useEffect(() => {
    const handleResize = () => {
      setSize({ width: window.innerWidth, height: window.innerHeight });
    };

    handleResize();
    window.addEventListener('resize', handleResize);

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

  return <p>{size.width} x {size.height}</p>;
}

Summary

Array Condition Behavior
None Every render
[] Mount only
[dep1, dep2] When a dependency changes

useEffect is the main tool for side effects. Next we will study useContext to manage global data.