React 19 Modern Hooks 45 min interactive guide⚓ Multi-Hook Lab Simulator

React Hooks: useState, useEffect, useRef & Custom Hooks

Master the complete React Hooks API. Learn the Rules of Hooks, state management with useState, lifecycle & side effects with useEffect, DOM access with useRef, global context with useContext, and building reusable Custom Hooks.

01

1. What Are React Hooks & Why Do We Need Them?

Before React 16.8, you had to write complex ES6 Class Components to manage state or lifecycle methods (`componentDidMount`, `componentWillUnmount`). React Hooks allow functional components to use state, effects, refs, and context directly using simple functions.

1. No `this` Binding

No more confusing `.bind(this)` or constructor boilerplate.

2. Reusable Logic

Extract stateful logic into custom hooks without changing component hierarchy.

3. Grouped Related Code

Keep event listeners and their cleanups in a single `useEffect` block.

03

3. The Rules of Hooks (Strict & Essential)

React relies on the exact order in which Hooks are called on every render to match state with the correct internal memory cell. To maintain this contract, you must follow two strict rules:

  • Rule 1: Only call Hooks at the top level of React function components or custom Hooks.
    Do NOT call Hooks inside:
    • Conditions (if (condition) { useState() })
    • Loops (for, while, items.forEach())
    • Nested functions (e.g. inside an onClick handler or callback)
    • try / catch / finally blocks
  • Rule 2: Only call Hooks from React functions. Call them only inside React functional components or your own Custom Hooks (functions starting with use). Never call Hooks inside regular vanilla JavaScript helper functions or class components.
🛡️ Linter Protection: The official eslint-plugin-react-hooks will automatically warn you in your editor if you accidentally violate these rules.
04

4. useState: Local Component State

useState declares a state variable that persists between renders. Calling the setter function schedules a re-render with the new value.

import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);

  // Functional update: use when next state depends on current state
  const handleAdd = () => setCount(prev => prev + 1);

  return <button onClick={handleAdd}>Count: {count}</button>;
}
05

5. useEffect: Synchronizing With External Systems

In modern React, useEffect is specifically designed to synchronize your component with external systems (browser APIs, network requests, timers, DOM event listeners, or third-party libraries).

💡 When NOT to use useEffect: Do NOT use effects to transform data for rendering (calculate derived state directly during render instead!) or to handle user events (handle user interactions directly in event handlers like onClick or onSubmit!).
Dependency ArraySynchronization BehaviorExternal System Example
useEffect(fn, [])Runs once after initial component mountSetting up a global window.addEventListener or WebSocket connection
useEffect(fn, [userId])Runs on mount + whenever userId changesSyncing document title or fetching data for a specific user ID
useEffect(fn)Runs after every single renderSyncing non-React third-party DOM widgets that require refresh on every change
useEffect(() => {
  // Synchronize with browser timer API
  const timer = setInterval(() => console.log("Tick"), 1000);

  // Cleanup function: cleans up external subscription when component unmounts
  return () => clearInterval(timer);
}, []);
06

6. useRef: DOM Access & Silent Persistence

useRef returns a mutable object with a .current property. It has two main uses:

// 1. Direct DOM node reference (focusing inputs, measuring elements)
const inputRef = useRef(null);
const focusInput = () => inputRef.current.focus();

// 2. Mutable value that persists across renders without triggering a re-render
const timerIdRef = useRef(null);
07

7. useContext: Global State Without Prop Drilling

// 1. Create Context
const ThemeContext = createContext("dark");

// 2. Provide Context at top level
<ThemeContext.Provider value="emerald">
  <App />
</ThemeContext.Provider>

// 3. Consume directly in any child component
const theme = useContext(ThemeContext);
08

8. useMemo & useCallback (Brief Performance Intro)

useMemo caches the result of an expensive calculation between renders. useCallback caches a function definition to avoid breaking reference equality for child components.

🚀 Modern React Note: With modern React (including the React Compiler in React 19), memoization is increasingly handled automatically by the compiler. You do not need to manually wrap every calculation or function in useMemo or useCallback. Only reach for manual memoization when profiling reveals a specific, measurable performance bottleneck.
// useMemo: caches expensive calculations
const filteredList = useMemo(() => filterMassiveDataset(data, query), [data, query]);

// useCallback: preserves function reference between renders
const handleSelect = useCallback((id) => setSelectedId(id), []);
10

10. Custom Hooks Architecture

Custom Hooks allow you to extract and share stateful logic between components without duplicating code:

// useWindowWidth.js - Reusable Custom Hook
export function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return width;
}
11

11. Choosing the Right Hook (Cheat Sheet)

What you need to accomplishThe Recommended Hook
Store dynamic data that updates the UI when changeduseState
Synchronize with external systems (APIs, timers, event listeners)useEffect
Access real DOM nodes directly or hold silent persistent valuesuseRef
Access shared global data (themes, auth) without prop drillinguseContext
Extract and share stateful business logic across componentsCustom Hook (use...)
Cache a heavy calculation if profiling shows laguseMemo (or let React Compiler handle it)
12

12. Common Hooks Mistakes

  • Using useEffect for user click actions: Network requests triggered by button clicks should live inside the onClick handler, not in an effect.
  • Using useEffect for derived state: Don't use state + effect to compute full name from first and last name; simply calculate const fullName = firstName + " " + lastName; during render.
  • Calling Hooks conditionally: Never put hooks inside if blocks or try/catch.
  • Missing effect cleanups: Forgetting to return a cleanup function when setting intervals or event listeners leads to memory leaks.
13

13. Building Practical Hook-Based Components & Best Practices

  • Keep effects minimal and focused: Each effect should manage one synchronization relationship.
  • Prefix custom hooks with use: Enables linter rule verification.
  • Treat state as immutable: Never mutate state or ref objects directly during render.
LIVE INTERACTIVE LAB

React Hooks Multi-Hook Playground

Compare `useState` vs `useEffect` vs `useRef` vs `useContext` in live interactive side-by-side modules!

`useState` State Manager
0
Current State: counter
Re-render Pipeline

Total Component Re-renders: 1

const [counter, setCounter] = useState(0);
🎯 React Hooks Challenge 1 of 5❌ Try Again

Goal 1: Click '+1 Increment' in Tab 1 to trigger a useState update.

TEST YOUR KNOWLEDGE

React Hooks Mastery Quiz

8 scenario-based questions testing your understanding of the Rules of Hooks, useState, useEffect dependencies, useRef DOM access, useContext, and custom hooks.

Question 1 of 8Score: 0 / 8
⚓ What are React Hooks?