Complete Reference · Free Download

React Hooks
Reference Guide

Every essential React Hook explained with real-world examples — useState, useEffect, useContext, useRef, useMemo, useCallback, useReducer, custom hooks and more. For beginners to advanced React developers.

Browse Hooks ↓
15+Hooks Covered
200+Code Examples
10 pagesPDF Download
FreeAlways
State Management

useState

Manage local component state. Returns a stateful value and a setter function — calling the setter triggers a re-render.

useState
Basic Usage
Declare state, read its value and update it with the setter.
jsx
import { useState } from 'react';

function Counter() {
  // [value, setter] = useState(initialValue)
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
      <button onClick={() => setCount(0)}>
        Reset
      </button>
    </div>
  );
}
useState
🧮
Functional Updates & Objects
Use functional updates when new state depends on previous state. Always spread objects.
jsx
// ✅ Functional update (safe for async)
setCount(prev => prev + 1);

// Object state — spread to keep props
const [user, setUser] = useState({
  name: 'Alice', age: 30
});

setUser(prev => ({
  ...prev,        // keep existing fields
  age: 31         // override one field
}));

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

// Add item
setItems(prev => [...prev, newItem]);

// Remove item
setItems(prev =>
  prev.filter(i => i.id !== id)
);
useState
🏗️
Lazy Initialisation
Pass a function to useState for expensive initial computations — runs only once.
jsx
// ❌ Runs every render (expensive)
const [data, setData] = useState(
  computeExpensiveValue()
);

// ✅ Lazy init — runs ONCE
const [data, setData] = useState(
  () => computeExpensiveValue()
);

// Real-world: read from localStorage
const [theme, setTheme] = useState(
  () => localStorage.getItem('theme')
         ?? 'dark'
);

// Boolean toggle pattern
const [open, setOpen] = useState(false);
const toggle = () =>
  setOpen(prev => !prev);
Side Effects

useEffect

Synchronise a component with an external system. Runs after every render by default; control it with the dependency array.

useEffect
🔄
Dependency Array Patterns
Control when your effect runs with the dependency array.
jsx
useEffect(() => {
  /* runs after EVERY render */
});

useEffect(() => {
  /* runs ONCE on mount */
}, []);

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

useEffect(() => {
  /* multiple deps */
  fetchData(page, filter);
}, [page, filter]);
useEffect
🧹
Cleanup Function
Return a cleanup function to cancel subscriptions, timers and event listeners.
jsx
useEffect(() => {
  // Subscribe
  const sub = store.subscribe(handler);

  // Cleanup on unmount / re-run
  return () => sub.unsubscribe();
}, []);

// Timer cleanup
useEffect(() => {
  const id = setInterval(tick, 1000);
  return () => clearInterval(id);
}, []);

// Event listener cleanup
useEffect(() => {
  window.addEventListener('resize', handleResize);
  return () =>
    window.removeEventListener('resize', handleResize);
}, []);
useEffect
🌍
Data Fetching Pattern
Fetch data inside useEffect with an AbortController for cleanup.
jsx
useEffect(() => {
  const ctrl = new AbortController();

  async function load() {
    try {
      setLoading(true);
      const res = await fetch(url, {
        signal: ctrl.signal
      });
      const data = await res.json();
      setData(data);
    } catch (e) {
      if (e.name !== 'AbortError')
        setError(e);
    } finally {
      setLoading(false);
    }
  }
  load();
  return () => ctrl.abort();
}, [url]);
Global State

useContext

Consume a React context without nesting. Pair with createContext and a provider to share state across the component tree.

useContext
🌐
Create & Provide Context
Set up a context and wrap the tree with its provider.
jsx
// 1. Create context with default value
const ThemeContext = createContext('light');

// 2. Provide value at top of tree
function App() {
  const [theme, setTheme] = useState('dark');

  return (
    <ThemeContext.Provider
      value={{ theme, setTheme }}>
      <Layout />
    </ThemeContext.Provider>
  );
}
useContext
🍃
Consume Context
Read context anywhere in the tree without prop drilling.
jsx
// 3. Consume — any depth, no drilling
function Button() {
  const { theme, setTheme } =
    useContext(ThemeContext);

  return (
    <button
      className={theme}
      onClick={() =>
        setTheme(t =>
          t === 'dark' ? 'light' : 'dark')
      }>
      Toggle
    </button>
  );
}
useContext
🏭
Custom Context Hook
Wrap useContext in a custom hook to encapsulate the context logic.
jsx
// auth-context.jsx
const AuthContext = createContext(null);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  return (
    <AuthContext.Provider
      value={{ user, setUser }}>
      {children}
    </AuthContext.Provider>
  );
}

// Custom hook with guard
export function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx)
    throw new Error('useAuth outside Provider');
  return ctx;
}
DOM & Persistence

useRef

Hold a mutable value that persists across renders without causing a re-render. Perfect for DOM references and previous-value tracking.

useRef
📌
DOM Reference
Directly access DOM nodes — focus, scroll, measure or animate.
jsx
function SearchBox() {
  const inputRef = useRef(null);

  useEffect(() => {
    // Focus input on mount
    inputRef.current?.focus();
  }, []);

  const handleClear = () => {
    inputRef.current.value = '';
    inputRef.current.focus();
  };

  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={handleClear}>
        Clear
      </button>
    </>
  );
}
useRef
🕰️
Mutable Values (No Re-render)
Store values that change but shouldn't cause re-renders — timer IDs, previous values.
jsx
// Store timer ID without re-render
const timerId = useRef(null);

const start = () => {
  timerId.current = setInterval(tick, 1000);
};
const stop = () => {
  clearInterval(timerId.current);
};

// Track previous value
function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current; // previous
}
useRef + forwardRef
🔭
forwardRef & useImperativeHandle
Forward refs to child components and expose custom imperative APIs.
jsx
// Child exposes custom methods
const FancyInput = forwardRef(
  (props, ref) => {
    const inputRef = useRef(null);

    useImperativeHandle(ref, () => ({
      focus: () => inputRef.current.focus(),
      clear: () => {
        inputRef.current.value = '';
      }
    }));

    return <input ref={inputRef} />;
  }
);

// Parent usage
const ref = useRef();
<FancyInput ref={ref} />
ref.current.focus();
Performance

useMemo

Memoize expensive computed values — only recomputes when dependencies change. Skip unnecessary re-computations.

useMemo
🧠
Memoize Computations
Cache the result of expensive calculations between renders.
jsx
const sortedList = useMemo(
  () => [...items].sort((a, b) =>
    a.name.localeCompare(b.name)
  ),
  [items]   // only re-sort when items changes
);

// Filter + map pipeline
const results = useMemo(() =>
  products
    .filter(p =>
      p.name.toLowerCase()
       .includes(query.toLowerCase()))
    .map(p => ({
      ...p,
      label: `${p.name} — $${p.price}`
    })),
  [products, query]
);
useMemo
⚖️
When to Use vs Skip
useMemo adds overhead — only use it when the computation is genuinely expensive.
✅ Use useMemo ❌ Skip it
Sort / filter large arrays Simple arithmetic
Heavy math / data transform String concatenation
Referential equality for children Primitive state values
Context value objects Functions (use useCallback)
D3 / canvas computations Small arrays (< 100 items)
useMemo
🏎️
Stable Object References
Prevent child re-renders caused by new object/array references on every render.
jsx
// ❌ New object every render
const config = { color: theme, size: 16 };
// Child re-renders even if theme unchanged
<Child config={config} />

// ✅ Stable reference
const config = useMemo(
  () => ({ color: theme, size: 16 }),
  [theme]
);

// Context value — avoid re-renders
const ctxValue = useMemo(
  () => ({ user, login, logout }),
  [user]
);
<Ctx.Provider value={ctxValue}> … </>
Performance

useCallback

Memoize function references — returns the same function unless dependencies change. Essential when passing callbacks to memoized children.

useCallback
🔗
Basic useCallback
Prevent child re-renders by keeping the same function reference.
jsx
// ❌ New fn every render → child re-renders
const handleClick = () => doSomething(id);

// ✅ Stable reference
const handleClick = useCallback(
  () => doSomething(id),
  [id]
);

// With React.memo child
const MemoChild = memo(function Child({ onClick }) {
  return <button onClick={onClick}>Click</button>;
});

// Parent
const handler = useCallback(() => {}, []);
<MemoChild onClick={handler} />
useCallback
🎯
useCallback vs useMemo
They're equivalent — useCallback(fn, deps) is useMemo(() => fn, deps).
Hook Returns Use For
useMemo Value Computed results
useCallback Function Event handlers, callbacks
jsx
// These are equivalent:
useCallback(fn, [deps]);
useMemo(() => fn, [deps]);

// useCallback for event handlers
const onSubmit = useCallback(async (e) => {
  e.preventDefault();
  await saveData(formData);
}, [formData]);
useCallback
📡
Stable Callbacks in Effects
Wrap callbacks used in useEffect deps to avoid infinite loops.
jsx
// ❌ Infinite loop — fetch recreated every render
const fetchData = () => 
api(id);

useEffect(() => { 
  fetchData(); 
}, [fetchData]);

// ✅ Stable with useCallback
const fetchData = 
useCallback(
  () => api(id), 
  [id]
);

useEffect(() => {
  fetchData();
}, [fetchData]); 

// only re-runs when id changes
Complex State

useReducer

Manage complex state logic with a reducer function. Great for state machines, multi-action updates, and when next state depends on action type.

useReducer
🔀
Reducer Pattern
Define state transitions with a pure reducer function and dispatch actions.
jsx
const initialState = { count: 0, step: 1 };

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state,
        count: state.count + state.step };
    case 'SET_STEP':
      return { ...state, step: action.payload };
    case 'RESET':
      return initialState;
    default:
      return state;
  }
}

const [state, dispatch] =
  useReducer(reducer, initialState);

dispatch({ type: 'INCREMENT' });
dispatch({ type: 'SET_STEP', payload: 5 });
useReducer
📋
Form State with Reducer
Manage complex form state, loading, and error handling in one reducer.
jsx
const init = {
  values: { 
    name: '', 
    email: '' 
  },
  loading: false, 
  error: null
};

function formReducer(
  state,
  action
) {
  switch (action.type) {

    case 'FIELD': 
      return {
        ...state,
        values: { 
          ...state.values,
          [action.field]: 
          action.value 
        }
      };

    case 'SUBMIT': 
      return {
        ...state, 
        loading: true, 
        error: null
      };

    case 'ERROR': 
      return {
        ...state, 
        loading: false,
        error: 
        action.message
      };

    default: 
      return state;

  }
}
useReducer
🏗️
useState vs useReducer
Choose the right tool based on state complexity and transition logic.
useState useReducer
Simple values Complex objects
Independent state Related state fields
Few transitions Many action types
Local only Shareable reducer logic
No history needed Undo / redo patterns
jsx
// Lazy init with 3rd arg
const [s, dispatch] = useReducer(
  reducer,
  userId,           // arg
  createInitialState // init fn
);
Reusability

Custom Hooks

Extract and reuse stateful logic across components. Any function starting with "use" that calls other hooks is a custom hook.

Custom Hook
🛠
useFetch — Data Fetching
Encapsulate fetch logic with loading, error and data state.
jsx
function useFetch(url) {
  const [data,    setData]    = useState(null);
  const [loading, setLoading] = useState(true);
  const [error,   setError]   = useState(null);

  useEffect(() => {
    const ctrl = new AbortController();
    fetch(url, { signal: ctrl.signal })
      .then(r => r.json())
      .then(d => { setData(d); setLoading(false); })
      .catch(e => {
        if (e.name !== 'AbortError') {
          setError(e); setLoading(false);
        }
      });
    return () => ctrl.abort();
  }, [url]);

  return { data, loading, error };
}

// Usage
const { data, loading } = useFetch('/api/users');
Custom Hook
💾
useLocalStorage
Sync state to localStorage — persistent across page reloads.
jsx
function useLocalStorage(key, initial) {
  const [value, setValue] = useState(() => {
    try {
      const item =
        localStorage.getItem(key);
      return item
        ? JSON.parse(item)
        : initial;
    } catch { return initial; }
  });

  const set = useCallback(v => {
    setValue(v);
    localStorage.setItem(
      key, JSON.stringify(v)
    );
  }, [key]);

  return [value, set];
}

// Usage
const [theme, setTheme] =
  useLocalStorage('theme', 'dark');
Custom Hook
⏲️
useDebounce & useWindowSize
Two essential utility hooks for search inputs and responsive layouts.
jsx
function useDebounce(value, delay) {
  const [debounced, set] = useState(value);
  useEffect(() => {
    const t = setTimeout(() => set(value), delay);
    return () => clearTimeout(t);
  }, [value, delay]);
  return debounced;
}

function useWindowSize() {
  const [size, setSize] = useState({
    w: window.innerWidth,
    h: window.innerHeight
  });
  useEffect(() => {
    const onResize = () => setSize({
      w: window.innerWidth,
      h: window.innerHeight
    });
    window.addEventListener('resize', onResize);
    return () =>
      window.removeEventListener('resize', onResize);
  }, []);
  return size;
}
Advanced APIs

Advanced Hooks

useLayoutEffect, useId, useDeferredValue, useTransition, useSyncExternalStore and more React 18+ hooks.

useLayoutEffect
📐
useLayoutEffect
Fires synchronously after DOM mutations — before the browser paints. Use for measurements.
jsx
// useEffect:       async, after paint
// useLayoutEffect: sync,  before paint
const ref = useRef(null);
const [height, setHeight] = useState(0);

useLayoutEffect(() => {
  // Read DOM before browser paints
  setHeight(ref.current.offsetHeight);
}, []);

// ✅ Good: prevent visual flicker
// ❌ Avoid on SSR (use useEffect)
// ❌ Don't fetch data here
React 18+
🚀
useTransition & useDeferredValue
Keep the UI responsive by marking state updates as non-urgent.
jsx
// useTransition — mark slow updates

const [
  isPending, 
  startTransition
] = useTransition();

const handleSearch = (
  e
) => {

  setInput(
    e.target.value
  ); 
  // urgent

  startTransition(() => {
    setResults(
      filter(
        e.target.value
      )
    );
  });

};

{isPending && <Spinner />}

// useDeferredValue — defer a value

const deferred = 
useDeferredValue(
  query
);

// deferred lags behind query —
// slow list renders with old value

React 18+
🆔
useId & useSyncExternalStore
Generate unique IDs and subscribe to external stores safely.
jsx
// useId — stable unique IDs (SSR-safe)

function FormField({
  label
}) {

  const id =
  useId();

  return (
    <>

    <label
      htmlFor={id}>
      {label}
    </label>

    <input
      id={id}
    />

    </>
  );

}

// useSyncExternalStore — subscribe

function
useOnlineStatus() {

  return
  useSyncExternalStore(

    navigator.onLine

    ? (cb) => {

      window.addEventListener(
        'offline',
        cb
      );

      return () =>
      window.removeEventListener(
        'offline',
        cb
      );

    }

    : (cb) => {

      window.addEventListener(
        'online',
        cb
      );

      return () =>
      window.removeEventListener(
        'online',
        cb
      );

    },

    () => navigator.onLine

  );

}

⚡ All Hooks Quick Reference

  • useState(init)Local state with setter
  • useEffect(fn, deps)Side effects after render
  • useContext(Ctx)Consume nearest provider value
  • useRef(init)Mutable ref, no re-render
  • useMemo(fn, deps)Cache expensive values
  • useCallback(fn, deps)Cache function references
  • useReducer(r, init)Complex state transitions
  • useLayoutEffectSync effect before paint
  • useTransition()Non-urgent state updates
  • useDeferredValue(v)Defer a value for slow UIs
  • useId()Stable unique SSR-safe ID
  • useSyncExternalStoreSubscribe to external stores
Best Practices

Rules of Hooks

Two mandatory rules enforced by the ESLint plugin — break them and you get subtle, hard-to-debug bugs.

📋
The Two Golden Rules
React relies on hook call order being the same every render.
jsx
// ❌ RULE 1 VIOLATION — conditional hook
if (condition) {
  const [x, setX] = useState(0); // 🚫
}

// ✅ Move condition INSIDE the hook
const [x, setX] = useState(
  condition ? 0 : null
);

// ❌ RULE 2 VIOLATION — hook in callback
function handleClick() {
  const [v, set] = useState(); // 🚫
}

// ❌ RULE 2 — hook in loop
items.forEach(item => {
  useEffect(() => {}); // 🚫
});

// ✅ Hooks at top level of component only
🗺️
Hook Decision Guide
Which hook should you reach for? Use this flowchart.
Situation Hook to Use
Store a UI value that triggers re-render useState
Complex state with multiple actions useReducer
Fetch data / subscribe to events useEffect
Share state across component tree useContext
Reference a DOM element useRef
Store value without re-render useRef
Cache expensive computation useMemo
Stable function reference useCallback
Measure DOM before paint useLayoutEffect
Keep UI responsive on slow updates useTransition
Unique IDs for accessibility useId
More Resources

Explore More Cheat Sheets

Keep levelling up with our other free developer references — hand-crafted by K2Infocom.

Get Your Free React Hooks PDF Guide

Join 50,000+ developers who download our cheat sheets every month. Get the full PDF in your inbox instantly — free forever.

Join WhatsApp Channel