Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
⚛️
Web Dev

React Hooks: Complete Notes

useState, useEffect, useContext, useRef, useMemo — every hook explained with real examples.

Back to Notes Library

React Hooks: Complete Notes

useState

tsx
import { useState } from 'react';

// Basic state
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const [user, setUser] = useState<User | null>(null);

// Functional update (when new state depends on old)
setCount(prev => prev + 1);

// Object state — always spread, never mutate!
const [form, setForm] = useState({ name: '', email: '' });
setForm(prev => ({ ...prev, name: 'Alice' }));

// Lazy initialization (expensive initial state)
const [data, setData] = useState(() => computeExpensiveValue());

useEffect

tsx
import { useEffect } from 'react';

// Run after every render
useEffect(() => {
  console.log("rendered");
});

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

// Run when dependencies change
useEffect(() => {
  fetchUser(userId);
}, [userId]);

// Cleanup (unsubscribe, clear timers)
useEffect(() => {
  const id = setInterval(() => tick(), 1000);
  return () => clearInterval(id); // cleanup function
}, []);

// Async in useEffect
useEffect(() => {
  let cancelled = false;
  async function load() {
    const data = await fetchData();
    if (!cancelled) setData(data);
  }
  load();
  return () => { cancelled = true; };
}, [id]);

useRef

tsx
import { useRef } from 'react';

// DOM reference
const inputRef = useRef<HTMLInputElement>(null);
// In JSX: <input ref={inputRef} />
inputRef.current?.focus();

// Mutable value (no re-render on change)
const timerRef = useRef<NodeJS.Timeout | null>(null);
timerRef.current = setTimeout(() => {}, 1000);

// Previous value pattern
function usePrevious<T>(value: T) {
  const ref = useRef<T>();
  useEffect(() => { ref.current = value; });
  return ref.current;
}

useContext

tsx
import { createContext, useContext, useState } from 'react';

// 1. Create context
interface ThemeContextType {
  theme: 'light' | 'dark';
  toggle: () => void;
}
const ThemeContext = createContext<ThemeContextType | null>(null);

// 2. Provide context (wrap your app or section)
function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');
  const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light');
  return (
    <ThemeContext.Provider value={{ theme, toggle }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 3. Use context in any child component
function ThemedButton() {
  const ctx = useContext(ThemeContext);
  if (!ctx) throw new Error("Must be inside ThemeProvider");
  return <button onClick={ctx.toggle}>Mode: {ctx.theme}</button>;
}

useMemo & useCallback

tsx
import { useMemo, useCallback } from 'react';

// useMemo — memoize expensive computation
const sortedList = useMemo(
  () => [...items].sort((a, b) => a.name.localeCompare(b.name)),
  [items] // recalculate only when items changes
);

// useCallback — memoize function reference
const handleClick = useCallback(
  (id: number) => {
    deleteItem(id);
    refetch();
  },
  [refetch] // new function only when refetch changes
);

When to use:

  • useMemo — Expensive calculations, avoid unnecessary re-renders of pure components
  • useCallback — Passing callbacks to optimized child components wrapped in React.memo

useReducer

tsx
import { useReducer } from 'react';

type State = { count: number; error: string | null };
type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'reset' }
  | { type: 'setError'; payload: string };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'increment': return { ...state, count: state.count + 1 };
    case 'decrement': return { ...state, count: state.count - 1 };
    case 'reset':     return { count: 0, error: null };
    case 'setError':  return { ...state, error: action.payload };
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0, error: null });
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </div>
  );
}

Custom Hooks

tsx
// useFetch — data fetching hook
function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    fetch(url)
      .then(r => r.json())
      .then(d => { if (!cancelled) setData(d); })
      .catch(e => { if (!cancelled) setError(e.message); })
      .finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [url]);

  return { data, loading, error };
}

// useLocalStorage — sync state with localStorage
function useLocalStorage<T>(key: string, initial: T) {
  const [value, setValue] = useState<T>(() => {
    try {
      const item = localStorage.getItem(key);
      return item ? JSON.parse(item) : initial;
    } catch { return initial; }
  });

  const setStored = (newValue: T) => {
    setValue(newValue);
    localStorage.setItem(key, JSON.stringify(newValue));
  };

  return [value, setStored] as const;
}

Rules of Hooks

  • Only call hooks at the top level (not inside loops, conditions, or nested functions)
  • Only call hooks from React function components or custom hooks
  • Custom hooks must start with "use"
  • ESLint plugin eslint-plugin-react-hooks enforces these rules automatically

Hook Quick Reference

HookPurpose
useStateLocal component state
useEffectSide effects, subscriptions
useRefDOM refs, mutable values
useContextGlobal state consumption
useMemoMemoize computed values
useCallbackMemoize function references
useReducerComplex state with actions
useIdUnique IDs for accessibility
useTransitionMark updates as non-urgent
useDeferredValueDefer expensive re-renders
📱

Get more notes like this daily on Telegram!

Free study notes, cheat sheets & AI tips

Join Free →
10K+ Members Growing Daily

Get Free AI Notes Daily

Join AiTechWorlds on Telegram and get daily AI tips, prompt engineering templates, coding resources, and exclusive content — 100% free!

📚 Free Study Notes🤖 AI Tips Daily⚡ Prompt Templates💻 Coding Resources
Join Free Channel

No spam. Leave anytime.

!