Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
24 minLesson 17 of 40
React Fundamentals

Hooks: useState & useEffect

Hooks: useState & useEffect

Hooks are functions that let you use React features in function components. useState manages local state; useEffect handles side effects like data fetching, subscriptions, and DOM interactions.

useState

import { useState } from "react";

// Basic counter
const [count, setCount] = useState(0);

// Object state
const [user, setUser] = useState({ name: "", email: "" });

// Array state
const [items, setItems] = useState([]);

// Boolean toggle
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(prev => !prev);

// Lazy initialization (expensive initial value computed once)
const [data, setData] = useState(() => JSON.parse(localStorage.getItem("data") ?? "[]"));

useEffect

useEffect runs after render. Use it for:

  • Fetching data
  • Setting up subscriptions
  • Updating the document title
  • Interacting with browser APIs
import { useState, useEffect } from "react";

function UserProfile({ userId }) {
    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    
    useEffect(() => {
        // This runs after every render where userId changed
        let cancelled = false;  // prevent race conditions
        
        setLoading(true);
        setError(null);
        
        fetch(`/api/users/${userId}`)
            .then(r => r.json())
            .then(data => {
                if (!cancelled) {
                    setUser(data);
                    setLoading(false);
                }
            })
            .catch(err => {
                if (!cancelled) {
                    setError(err.message);
                    setLoading(false);
                }
            });
        
        // Cleanup function — runs before next effect or unmount
        return () => { cancelled = true; };
        
    }, [userId]);  // dependency array — re-run when userId changes
    
    if (loading) return <p>Loading...</p>;
    if (error) return <p>Error: {error}</p>;
    if (!user) return null;
    
    return <div>{user.name}</div>;
}

Dependency Array

// Run on every render (usually wrong)
useEffect(() => {
    document.title = `${count} items`;
});

// Run once on mount
useEffect(() => {
    analytics.trackPageView();
}, []);

// Run when specific values change
useEffect(() => {
    fetchUsers(filters);
}, [filters]);  // re-fetch when filters change

// Run when multiple values change
useEffect(() => {
    updateChart(data, options);
}, [data, options]);

Common Patterns

Document Title

useEffect(() => {
    document.title = `${count} notifications | MyApp`;
    return () => { document.title = "MyApp"; };
}, [count]);

Resize Observer

useEffect(() => {
    function handleResize() {
        setWindowSize({ width: window.innerWidth, height: window.innerHeight });
    }
    
    window.addEventListener("resize", handleResize);
    handleResize();  // call immediately
    
    return () => window.removeEventListener("resize", handleResize);
}, []);  // only mount/unmount
function SearchComponent() {
    const [query, setQuery] = useState("");
    const [results, setResults] = useState([]);
    
    useEffect(() => {
        if (!query.trim()) { setResults([]); return; }
        
        const timer = setTimeout(async () => {
            const data = await fetch(`/api/search?q=${query}`).then(r => r.json());
            setResults(data);
        }, 300);
        
        return () => clearTimeout(timer);  // cancel on new keystroke
    }, [query]);
    
    return (
        <div>
            <input value={query} onChange={e => setQuery(e.target.value)} />
            <ul>{results.map(r => <li key={r.id}>{r.title}</li>)}</ul>
        </div>
    );
}

Rules of Hooks

  1. Only call hooks at the top level — never inside if, loops, or nested functions
  2. Only call hooks in React functions — not in regular JS functions
// ❌ Wrong
function Component({ condition }) {
    if (condition) {
        const [state, setState] = useState(0);  // conditional hook — never do this!
    }
}

// ✅ Correct
function Component({ condition }) {
    const [state, setState] = useState(0);  // always called
    
    if (!condition) return null;
    return <div>{state}</div>;
}

Next lesson: Lists, Keys & Conditional Rendering — rendering dynamic data in React.

📱

Get this course's notes on Telegram!

Free cheat sheets, summaries & practice exercises

Get Notes Free →
!