← All Tools

⚛️ React Hooks Cheat Sheet

49+ examples • All built-in hooks + React 19 • Custom hook patterns

useState 8useEffect 5useRef 4useMemo & useCallback 5useContext 4useReducer 4React 18+ Hooks 5React 19 Hooks 4Custom Hooks 5Patterns & Tips 5

useState 8

Basic state
const [count, setCount] = useState(0);
Typed state (TypeScript)
const [user, setUser] = useState<User | null>(null);
Array state
const [items, setItems] = useState<string[]>([]);
Functional update (prev state)
setCount(prev => prev + 1);
Partial object update
setUser({ ...user, name: 'Alice' });
Append to array
setItems(prev => [...prev, newItem]);
Remove from array
setItems(prev => prev.filter(i => i.id !== id));
Lazy initial state
const [state, setState] = useState(() => {
  return expensiveComputation();
});

useEffect 5

Run on dependency change
useEffect(() => {
  document.title = `Count: ${count}`;
}, [count]);
Run once on mount
useEffect(() => {
  fetchData();
}, []);
Cleanup (timer)
useEffect(() => {
  const id = setInterval(tick, 1000);
  return () => clearInterval(id);
}, []);
Cleanup (fetch abort)
useEffect(() => {
  const ctrl = new AbortController();
  fetch(url, { signal: ctrl.signal })
    .then(r => r.json())
    .then(setData);
  return () => ctrl.abort();
}, [url]);
Cleanup (event listener)
useEffect(() => {
  const handler = (e: KeyboardEvent) => {
    if (e.key === 'Escape') setOpen(false);
  };
  window.addEventListener('keydown', handler);
  return () => window.removeEventListener('keydown', handler);
}, []);

useRef 4

DOM element ref
const inputRef = useRef<HTMLInputElement>(null);
// <input ref={inputRef} />
// inputRef.current?.focus();
Store previous value
const prevValue = useRef(value);
useEffect(() => {
  prevValue.current = value;
}, [value]);
Track mount status
const isMounted = useRef(true);
useEffect(() => {
  return () => { isMounted.current = false; };
}, []);
Store timer ID
const timerRef = useRef<number>();
timerRef.current = window.setTimeout(fn, 1000);
// clearTimeout(timerRef.current);

useMemo & useCallback 5

Memoize expensive computation
const sorted = useMemo(
  () => items.sort((a, b) => a.name.localeCompare(b.name)),
  [items]
);
Memoize filtered list
const filtered = useMemo(
  () => users.filter(u => u.name.includes(query)),
  [users, query]
);
Memoize callback
const handleClick = useCallback(() => {
  setCount(c => c + 1);
}, []);
Callback with dependency
const handleSubmit = useCallback((data: FormData) => {
  api.save(id, data);
}, [id]);
React 19 note
// React 19: useCallback/useMemo often unnecessary
// React Compiler auto-memoizes

useContext 4

Create context
const ThemeContext = createContext<'light' | 'dark'>('light');
Provide context
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Child />
    </ThemeContext.Provider>
  );
}
Consume context
const theme = useContext(ThemeContext);
Safe context hook
// Custom hook pattern
function useTheme() {
  const ctx = useContext(ThemeContext);
  if (!ctx) throw new Error('useTheme must be inside ThemeProvider');
  return ctx;
}

useReducer 4

Typed reducer
type Action = 
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'reset'; payload: number };

function reducer(state: number, action: Action): number {
  switch (action.type) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    case 'reset': return action.payload;
  }
}
Use reducer
const [state, dispatch] = useReducer(reducer, 0);
Dispatch actions
dispatch({ type: 'increment' });
dispatch({ type: 'reset', payload: 0 });
Lazy initialization
const [state, dispatch] = useReducer(reducer, initialArg, init);

React 18+ Hooks 5

useId — unique IDs for accessibility
const id = useId();
// <label htmlFor={id}>Name</label>
// <input id={id} />
useTransition — non-blocking updates
const [isPending, startTransition] = useTransition();
startTransition(() => {
  setSearchResults(filterData(query));
});
useDeferredValue — defer expensive renders
const deferredQuery = useDeferredValue(query);
const results = useMemo(
  () => filterItems(deferredQuery),
  [deferredQuery]
);
useSyncExternalStore — external store
useSyncExternalStore(
  store.subscribe,
  store.getSnapshot,
  store.getServerSnapshot
);
useInsertionEffect — CSS-in-JS libraries
useInsertionEffect(() => {
  const style = document.createElement('style');
  style.textContent = css;
  document.head.appendChild(style);
  return () => style.remove();
}, [css]);

React 19 Hooks 4

useActionState — form actions
const [state, formAction, isPending] = useActionState(
  async (prev, formData) => {
    const result = await saveUser(formData);
    return result;
  },
  initialState
);
useOptimistic — optimistic updates
const [optimisticName, setOptimistic] = useOptimisticState(name);
// Instantly update UI before server responds
setOptimistic('New Name');
use() — read promises/context
// use() can read promises and context
const data = use(fetchPromise);
const theme = use(ThemeContext);
ref as prop (React 19)
// ref as prop (no forwardRef needed in React 19)
function Input({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

Custom Hooks 5

useLocalStorage
function useLocalStorage<T>(key: string, initial: T) {
  const [value, setValue] = useState<T>(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initial;
  });
  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);
  return [value, setValue] as const;
}
useDebounce
function useDebounce<T>(value: T, delay: number): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);
  return debounced;
}
useFetch
function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  useEffect(() => {
    const ctrl = new AbortController();
    fetch(url, { signal: ctrl.signal })
      .then(r => r.json())
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
    return () => ctrl.abort();
  }, [url]);
  return { data, loading, error };
}
useMediaQuery
function useMediaQuery(query: string): boolean {
  const [matches, setMatches] = useState(
    () => window.matchMedia(query).matches
  );
  useEffect(() => {
    const mql = window.matchMedia(query);
    const handler = (e: MediaQueryListEvent) => setMatches(e.matches);
    mql.addEventListener('change', handler);
    return () => mql.removeEventListener('change', handler);
  }, [query]);
  return matches;
}
useOnClickOutside
function useOnClickOutside(
  ref: RefObject<HTMLElement>,
  handler: () => void
) {
  useEffect(() => {
    const listener = (e: MouseEvent) => {
      if (!ref.current?.contains(e.target as Node)) handler();
    };
    document.addEventListener('mousedown', listener);
    return () => document.removeEventListener('mousedown', listener);
  }, [ref, handler]);
}

Patterns & Tips 5

Immutable state updates
// ❌ Don't mutate state directly
state.items.push(item);

// ✅ Create new reference
setState({ ...state, items: [...state.items, item] });
Avoid stale closures
// ❌ Stale closure
useEffect(() => {
  setInterval(() => console.log(count), 1000);
}, []);

// ✅ Use ref for latest value
const countRef = useRef(count);
countRef.current = count;
Derive, don't sync
// ❌ Unnecessary effect
useEffect(() => {
  setFullName(first + ' ' + last);
}, [first, last]);

// ✅ Derive during render
const fullName = first + ' ' + last;
Rules of Hooks
// Rules of Hooks:
// 1. Only call at top level (no if/for/nested)
// 2. Only call in React functions or custom hooks
// 3. Custom hooks must start with 'use'
Conditional hooks (don't!)
// Conditional rendering with hooks
const Component = ({ show }: { show: boolean }) => {
  // ❌ if (show) { const [x] = useState(0); }
  const [x] = useState(0); // ✅ always call
  if (!show) return null;
  return <div>{x}</div>;
};

Made with ♥ by Kas Developer Tools

\xF0\x9F\x92\x99 Tip\xF0\x9F\x93\x9A Get Bundle \x244.99