πŸ“‹ Cheat Sheets

React Hooks Cheat Sheet β€” Every Hook Explained with Examples


Click any hook to expand the explanation and examples.

πŸ”„ State Hooks

useState essential
Add state to a functional component.
const [count, setCount] = useState(0);
const [user, setUser] = useState(null);
const [items, setItems] = useState([]);

// Update with new value setCount(5);

// Update based on previous state setCount(prev => prev + 1);

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

Always use the callback form (prev => …) when the new state depends on the old state.

useReducer essential
Manage complex state logic with a reducer function.
function reducer(state, action) {
  switch (action.type) {
    case 'increment': return { count: state.count + 1 };
    case 'decrement': return { count: state.count - 1 };
    case 'reset': return { count: 0 };
    default: throw new Error('Unknown action');
  }
}

const [state, dispatch] = useReducer(reducer, { count: 0 });

dispatch({ type: β€˜increment’ }); dispatch({ type: β€˜reset’ });

Prefer useReducer over useState when you have multiple related state values or complex update logic.

⚑ Effect Hooks

useEffect essential
Run side effects after render (data fetching, subscriptions, DOM manipulation).
// Run on every render
useEffect(() => {
  console.log('rendered');
});

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

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

// Cleanup (runs before next effect and on unmount) useEffect(() => { const sub = subscribe(channel); return () => sub.unsubscribe(); }, [channel]);

Always include all values from the component scope that change over time in the dependency array.

useLayoutEffect advanced
Like useEffect, but fires synchronously after DOM mutations, before the browser paints.
useLayoutEffect(() => {
  const { height } = ref.current.getBoundingClientRect();
  setHeight(height);
}, []);
Use this only when you need to read layout from the DOM and re-render synchronously before the user sees the update. Prefer useEffect in most cases.

πŸ”— Context

useContext essential
Read and subscribe to context from a component.
const ThemeContext = createContext('light');

// Provider (parent) <ThemeContext.Provider value=β€œdark”> <App /> </ThemeContext.Provider>

// Consumer (any child) function Button() { const theme = useContext(ThemeContext); return <button className={theme}>Click</button>; }

The component re-renders whenever the context value changes.

πŸ“Œ Ref Hooks

useRef essential
Hold a mutable value that persists across renders without causing re-renders.
// DOM reference
const inputRef = useRef(null);
<input ref={inputRef} />
inputRef.current.focus();

// Mutable value (like instance variable) const intervalId = useRef(null); intervalId.current = setInterval(tick, 1000); clearInterval(intervalId.current);

// Previous value pattern const prevCount = useRef(count); useEffect(() => { prevCount.current = count; });

Changing .current does not trigger a re-render.

useImperativeHandle advanced
Customize the ref value exposed to parent components.
const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current.focus(),
    clear: () => { inputRef.current.value = ''; }
  }));
  return <input ref={inputRef} />;
});

// Parent can now call ref.current.focus(); ref.current.clear();

Use sparingly β€” prefer passing props over imperative handles.

πŸš€ Performance Hooks

useMemo performance
Cache the result of an expensive computation.
const sortedList = useMemo(() => {
  return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);

const filteredUsers = useMemo(() => { return users.filter(u => u.active); }, [users]);

Only use when you have a measurably expensive calculation. Don’t wrap everything in useMemo.

useCallback performance
Cache a function definition between re-renders.
const handleClick = useCallback((id) => {
  setItems(prev => prev.filter(item => item.id !== id));
}, []);

// Useful when passing callbacks to memoized children <MemoizedChild onClick={handleClick} />

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps). Mainly useful when passing callbacks to components wrapped in React.memo.

πŸ†• React 18+ Hooks

useId react18
Generate unique IDs that are stable across server and client rendering.
function FormField({ label }) {
  const id = useId();
  return (
    <>
      <label htmlFor={id}>{label}</label>
      <input id={id} />
    </>
  );
}
Don't use useId for keys in a list β€” use your data's unique identifier instead.
useTransition react18
Mark a state update as non-urgent so the UI stays responsive.
const [isPending, startTransition] = useTransition();

function handleSearch(query) { startTransition(() => { setSearchResults(filterLargeList(query)); }); }

return ( <> <input onChange={e => handleSearch(e.target.value)} /> {isPending ? <Spinner /> : <Results data={searchResults} />} </> );

The input stays responsive while the expensive list filtering happens in the background.

useDeferredValue react18
Defer updating a part of the UI to keep the rest responsive.
function Search({ query }) {
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;

return ( <div style={{ opacity: isStale ? 0.5 : 1 }}> <HeavyList query={deferredQuery} /> </div> ); }

Similar to useTransition but for values you receive as props rather than state you control.

useSyncExternalStore react18
Subscribe to an external store (for library authors).
const width = useSyncExternalStore(
  // subscribe
  (callback) => {
    window.addEventListener('resize', callback);
    return () => window.removeEventListener('resize', callback);
  },
  // getSnapshot (client)
  () => window.innerWidth,
  // getServerSnapshot (SSR)
  () => 1024
);
Primarily used by state management libraries (Redux, Zustand) to integrate with React's concurrent features.

πŸ› οΈ Custom Hooks

useLocalStorage pattern
Sync state with localStorage.
function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });
  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);
  return [value, setValue];
}

// Usage const [theme, setTheme] = useLocalStorage(β€˜theme’, β€˜light’);

useFetch pattern
Reusable data fetching hook.
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

useEffect(() => { const controller = new AbortController(); setLoading(true); fetch(url, { signal: controller.signal }) .then(res => res.json()) .then(setData) .catch(setError) .finally(() => setLoading(false)); return () => controller.abort(); }, [url]);

return { data, loading, error }; }

// Usage const { data, loading, error } = useFetch(β€˜/api/users’);

useMediaQuery pattern
Respond to CSS media queries in JavaScript.
function useMediaQuery(query) {
  const [matches, setMatches] = useState(
    () => window.matchMedia(query).matches
  );
  useEffect(() => {
    const mql = window.matchMedia(query);
    const handler = (e) => setMatches(e.matches);
    mql.addEventListener('change', handler);
    return () => mql.removeEventListener('change', handler);
  }, [query]);
  return matches;
}

// Usage const isMobile = useMediaQuery(β€˜(max-width: 768px)’);

useDebounce pattern
Debounce a rapidly changing value.
function useDebounce(value, delay = 300) {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);
  return debounced;
}

// Usage const [query, setQuery] = useState(”); const debouncedQuery = useDebounce(query, 500); useEffect(() => { search(debouncedQuery); }, [debouncedQuery]);

πŸ“ Rules of Hooks

Rule 1: Only call at the top level rules
Never call hooks inside loops, conditions, or nested functions.
// ❌ Wrong
if (loggedIn) {
  useEffect(() => { ... });
}

// βœ… Correct useEffect(() => { if (loggedIn) { … } }, [loggedIn]);

React relies on the order hooks are called to match state to the correct hook.

Rule 2: Only call from React functions rules
Only call hooks from React function components or custom hooks.
// ❌ Wrong β€” regular function
function getUser() {
  const [user, setUser] = useState(null);
}

// βœ… Correct β€” component function UserProfile() { const [user, setUser] = useState(null); }

// βœ… Correct β€” custom hook (starts with β€œuse”) function useUser() { const [user, setUser] = useState(null); return user; }

Custom hooks must start with use so React can check for rule violations.

Rule 3: Exhaustive dependencies rules
Include all reactive values in the dependency array.
// ❌ Missing dependency
useEffect(() => {
  fetchUser(userId);
}, []);

// βœ… Correct useEffect(() => { fetchUser(userId); }, [userId]);

Use the eslint-plugin-react-hooks plugin β€” it catches these automatically.

Quick Reference Table

HookPurpose
useStateSimple state
useReducerComplex state logic
useEffectSide effects (fetch, subscribe)
useLayoutEffectSynchronous DOM reads
useContextRead context values
useRefDOM refs / mutable values
useMemoCache expensive computations
useCallbackCache function references
useIdGenerate unique IDs
useTransitionNon-urgent state updates
useDeferredValueDefer re-rendering
useSyncExternalStoreSubscribe to external stores
useImperativeHandleCustomize exposed ref