DEV Community

Mahdar
Mahdar

Posted on

React Hooks: A Comprehensive Cheatsheet & Guide

React Hooks revolutionized how we write components, moving logic from complex class lifecycles to clean, reusable functions. But with so many hooks (especially with React 19!), it's easy to get lost.

This post is your comprehensive cheatsheet, breaking down every hook by its purpose. Bookmark this page—you'll come back to it!


The Rules of Hooks

Before you start, remember the two golden rules (with one new exception):

  1. Only Call Hooks at Top Level (not in loops/conditionals/nested functions)
  2. Only Call from React Functions (components or custom hooks)
  3. use hook is the exception can be called conditionally!
// ✅ Correct
function MyComponent() {
  const [count, setCount] = useState(0); // top-level
}

// ❌ Wrong
if (condition) {
  const [count, setCount] = useState(0); // don't do this
}

Enter fullscreen mode Exit fullscreen mode

STATE: Managing Component State

Hooks for storing and updating values within your component.

useState → The basic state hook. Use it for simple, local component state (strings, numbers, booleans, arrays).
useReducer → Manages complex state logic, especially when the next state depends on the previous one. A great alternative to useState for complex objects or when state transitions are well-defined (think: Redux-lite for a single component).

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    default:
      return state;
  }
}

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

Enter fullscreen mode Exit fullscreen mode

ACTION & FORM: Hooks for Forms (React 19+)

A new suite of hooks designed to make form handling and server actions seamless.

useActionState → Manages the state of a form action. It gives you the pending state, the error message, and the returned data from the action, all in one.

const [state, formAction] = useActionState(async (formData) => {
  const result = await submitForm(formData);
  return result;
});

Enter fullscreen mode Exit fullscreen mode

useFormStatus → Used inside a <form>, this hook gives you the pending status of the parent form. Perfect for disabling a submit button or showing a spinner.

useOptimistic → Lets you "optimistically" update the UI before an async action completes. For example, show a new message in a chat list immediately while the server request is still pending. React will automatically revert it if the action fails.

Optimistically update UI before async actions complete.

const [messages, setMessages] = useOptimistic(initialMessages);

async function handleSend(newMessage) {
  setMessages([...messages, newMessage]); // show instantly
  await sendToServer(newMessage); // sync with backend
}

Enter fullscreen mode Exit fullscreen mode

EFFECT: Side Effects & Lifecycle

Hooks for interacting with the "outside world" (APIs, subscriptions, the DOM).

useEffect → Run side effects after render. This is your go-to for data fetching, setting up subscriptions, or manually manipulating the DOM. Don't forget the cleanup function!

useEffectEvent → Creates a stable function to read the latest state/props inside an effect.

When to use: When you need to call a function inside useEffect that uses props/state, but you don't want the effect to re-run when that prop/state changes. (Solves the "dependency array" problem).

useLayoutEffect → Runs synchronously after all DOM mutations but before the browser paints. Use this only when you need to read layout from the DOM (like getting an element's size) and synchronously re-render.** Use sparingly as it blocks painting.**

useInsertionEffect → Runs before useLayoutEffect and before any DOM mutations. This is almost exclusively for CSS-in-JS libraries to inject <style> tags. You will probably never use this directly.


ASYNC: Resource Loading (React 19+)

A new paradigm for handling async data and resources.

use → The revolutionary hook. It "unwraps" the value of a Promise (for data) or reads a Context. Unlike other hooks, it can be used in loops and conditionals. It's the key to client-side data fetching with Suspense.


PERF: Performance & Memoization

Hooks to skip unnecessary work and keep your app fast.

useMemo → Caches (memoizes) the result of an expensive calculation. It re-runs the calculation only if one of its dependencies changes.

useCallback → Caches a function definition. This is vital when passing callbacks to optimized child components (like React.memo) to prevent them from re-rendering unnecessarily.

useTransition → Marks state updates as "non-urgent." This keeps your UI responsive (like keeping a text input fast) while a "heavy" update (like re-rendering a large list) happens in the background.

useDeferredValue → Defers updating a non-critical part of the UI. Similar to useTransition but used when you don't control the state update itself (e.g., the value is a prop) debouncing alternative.

// useTransition
const [isPending, startTransition] = useTransition();

startTransition(() => {
  setFilteredItems(expensiveFilter(items));
});

// useDeferredValue
const deferredSearch = useDeferredValue(searchTerm);

Enter fullscreen mode Exit fullscreen mode

Refs & Imperative Access

Hooks for accessing DOM nodes or storing values that don't cause re-renders.

useRef → Returns a mutable ref object. Its .current property can hold a value that persists across renders without triggering a re-render. Perfect for accessing DOM elements (e.g., myInput.current.focus()) or storing timer IDs.

useImperativeHandle → Customizes the instance value that is exposed to parent components when using ref. Used with forwardRef to create a custom, limited API for your component.


OTHER: Context, Identity & External Stores

Utility hooks for solving specific problems.

useContext → Reads and subscribes to a React Context. The simplest way to consume shared data (like a theme or user auth) and avoid "prop drilling."

useId → Generates a unique, stable ID. Essential for accessibility attributes (like connecting <label>s to <input>s) and preventing ID mismatches during Server-Side Rendering (SSR).

useSyncExternalStore → Subscribes to an external store (like Zustand, Redux, or even window.matchMedia) in a way that is compatible with concurrent rendering.

const online = useSyncExternalStore(
  (listener) => window.addEventListener("online", listener),
  () => navigator.onLine
);

Enter fullscreen mode Exit fullscreen mode

That's the list! Don't try to memorize them all. Use this guide as a reference. The best way to learn is to build.

What's your most-used hook? Any tricky ones I missed? Let me know in the comments!

Top comments (0)