React Hooks

useState, useEffect, useMemo, useCallback, useRef, useReducer, useContext

Essential Hooks

tsx
// Hook Patterns
import { useState, useEffect, useMemo, useCallback, useRef, useReducer, useContext, createContext } from "react";

// useState — simple state
const [query, setQuery] = useState("");

// useEffect — side effects (API calls, subscriptions, DOM)
useEffect(() => {
  const controller = new AbortController();
  fetch(`/api/search?q=${query}`, { signal: controller.signal })
    .then(r => r.json())
    .then(setResults);
  return () => controller.abort(); // cleanup
}, [query]); // re-run when query changes

// useRef — persist value without re-render
const inputRef = useRef<HTMLInputElement>(null);
const renderCount = useRef(0);
useEffect(() => { renderCount.current++; });
// inputRef.current?.focus()

// useMemo — memoize expensive computation
const sorted = useMemo(
  () => items.sort((a, b) => a.name.localeCompare(b.name)),
  [items]
);

// useCallback — memoize function reference
const handleSubmit = useCallback((data: FormData) => {
  api.submit(data);
}, []);

// useReducer — complex state logic
type Action = { type: "increment" } | { type: "decrement" } | { type: "reset" };
function reducer(state: number, action: Action) {
  switch (action.type) {
    case "increment": return state + 1;
    case "decrement": return state - 1;
    case "reset": return 0;
  }
}
const [count, dispatch] = useReducer(reducer, 0);

// useContext — global state without prop drilling
const ThemeContext = createContext<"light" | "dark">("light");
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <DeepChild />
    </ThemeContext.Provider>
  );
}
function DeepChild() {
  const theme = useContext(ThemeContext); // "dark"
}

Hook Rules

  • Only call hooks at the top level — never inside loops, conditions, or nested functions
  • Only call hooks from React components or custom hooks
  • Custom hooks must start with 'use' prefix
  • Dependencies array: [] = run once, [dep] = run when dep changes, no array = every render

💬 When to use useMemo vs useCallback?

useMemo memoizes a computed value (e.g., filtered/sorted list). useCallback memoizes a function reference. Use useCallback when passing functions to child components to prevent unnecessary re-renders. Both take a dependency array.