ALL React Hooks Explained: When, Why, and How to Use Every Hook in Your React Toolkit

ALL React Hooks Explained: When, Why, and How to Use Every Hook in Your React Toolkit

Written by Massa Medi

So, you’ve decided to build a React app. Fantastic! That likely means you’ll spend some quality time with React hooks—the powerful functions that let you manage state, side effects, refs, context, and more within your components. The only catch: React has quite a bouquet of hooks, and it’s not always obvious which one to reach for in a given situation.

Let’s embark on a guided tour through every major React hook. We’ll explore exactly what each hook does, outline the best ways to use them, and break down how often you’ll find them useful—from the go-to, daily-use hooks to the rare gems you might only need for advanced scenarios. To make this grand tour easier, I’ve built a mental “map” of React hooks, grouping them into eight major categories. You’ll see how each fits into the bigger picture as you build scalable, maintainable React apps.

State Management Hooks: useState, useReducer, and useSyncExternalStore

useState is the bread and butter of any React developer. If you’re not using a larger framework for state management, you’ll use useState a lot. In fact, the very reason React exists is to help us manage state and re-render components as that state changes.

useState shines in client components with their own simple, component-specific state. Its versatility is unmatched: capture user input in form fields, show or hide modals, tooltips, or dropdowns with booleans, conditionally add CSS classes, or keep track of numbers for things like shopping carts or counters.

To use useState, you provide an initial value (really, any JavaScript value)—that value lands in a state variable, which React keeps track of. You’ll receive an array with two items: the state value and a function to update it. In code, you typically destructure them like so:

const [count, setCount] = useState(0);

Whenever you call the updater function, React re-renders the component with the new state.

useReducer is another state hook, but you’ll reach for it less often. It works wonders for more complex, interrelated state—think forms with lots of inputs, or game states where several values change together. useReducer takes a reducer function (which receives state and an action, then returns new state) and an initial value. It returns the current state and a dispatch function to trigger updates.

This centralizes all state updates in a single function, making your codebase much easier to maintain as your apps scale. For example:


  const initialState = { count: 0 };
  function reducer(state, action) {
    switch (action.type) {
      case 'increment': return { count: state.count + 1 };
      default: return state;
    }
  }
  const [state, dispatch] = useReducer(reducer, initialState);
          

useSyncExternalStore, on the other hand, is—let’s be honest—pretty niche. You probably won’t use it unless you’re writing your own state management solution from scratch, or need to sync non-React data stores into React’s ecosystem. Most developers can safely skip this one.

Effect Hooks: useEffect, useLayoutEffect, and useInsertionEffect

Time to talk side effects! “Side effects” are any interactions with systems outside of React—like updating the browser title, fetching data, or synchronizing with browser APIs.

useEffect is the familiar workhorse here. You give it a function to run, and by default, React runs it after every render. You can limit when it runs by supplying a dependencies array. When a value in that array changes, the effect runs again. For example, changing the browser tab’s title when a button is clicked:


  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);
          

There are two broad types of side effects:

But here’s a power tip: in modern React, don’t use useEffect for everything! For event-based effects, put the logic straight in your event handler. For data fetching, libraries like React Query or your framework’s own tools (such as Next.js data fetching methods) are often superior.

So when should you use useEffect? Primarily to synchronize React with browser APIs or other external systems. For example, synchronizing component state with the browser’s media API to play or pause a video.

useLayoutEffect is a special variant that runs before React paints the UI, synchronously. It’s useful when you need to measure or update the DOM before the user sees any changes—such as measuring the height of a tooltip and setting it in state before it appears onscreen. (This technique ensures the layout is accurate on first visual paint!)

useInsertionEffect is even more niche, usually relevant only to library authors creating CSS-in-JS libraries like Styled Components or Framer Motion. This hook runs before useEffect and useLayoutEffect, allowing the insertion of styles just in time for them to be read by other effect hooks.

Ref Hooks: useRef and useImperativeHandle

Refs let you hang on to a value across renders without triggering re-renders if that value changes. useRef is a bit like useState, but for non-display data. It returns a single object with a current property:

const timerRef = useRef(null);

You can set timerRef.current directly, making this perfect for non-stateful data like interval IDs, or referencing DOM elements. Here’s how you might store an interval ID for a timer, so you can clear it in a stopTimer function.

useRef also shines for DOM access—just connect the returned ref to the ref prop of any React element, and then use ref.current to directly access the underlying DOM node.

useImperativeHandle, meanwhile, is quite rare. It’s used when a parent needs to call a function (like focus()) on a child component via refs. To do this, you combine forwardRef with useImperativeHandle—a pattern that lets you expose only specific functions (not the whole instance) to the parent. Note: in React 19, this whole (slightly clunky) dance gets much simpler.

Performance Hooks: useMemo and useCallback

When it comes to optimizing React applications, two hooks deserve special mention: useMemo and useCallback.

useMemo helps you avoid expensive recalculations by memoizing the result of a function—only recomputing it when its dependencies change. Think: running an intensive calculation (like summing a large array) only when absolutely necessary.

const sum = useMemo(() => numbers.reduce((a, b) => a + b, 0), [numbers]);

useMemo looks similar to useEffect, but it always returns a value and never produces side effects.

useCallback is closely related. Instead of memoizing a value, it memoizes a function. This is especially important for callback functions passed to deeply nested child components. Without it, a new function gets created on every render—sometimes triggering unnecessary renders in optimized child components.

const increment = useCallback(() => setCount(c => c + 1), []);

Pass increment to your <Button /> components with confidence—React won’t re-create the function unless its dependencies change.

Context Hook: useContext

useContext lets you read values from a React Context. It’s incredibly simple: as long as your component is rendered inside a Provider (no matter how many components deep), you can call useContext to access the context’s value:

const theme = useContext(ThemeContext);

That’s it! It’s a clean and powerful way to share data down the tree without prop drilling.

Transition Hooks: useTransition and useDeferredValue

Traditionally, any state update in React is treated as urgent—everything re-renders immediately. But what if you’re running a really heavy computation or updating a giant list? That can make your UI feel sluggish or unresponsive.

useTransition offers a way to mark certain state updates as “not urgent.” You wrap these updates in a startTransition callback. React knows it can defer this work until a less busy moment—keeping your app snappy. It also provides an isPending boolean so you can show loading indicators while transitions are happening. For example, filtering a list as users type, without making every keystroke slow things down:


  const [isPending, startTransition] = useTransition();
  
  function handleInputChange(e) {
    startTransition(() => {
      setFilter(e.target.value);
    });
  }
          

useDeferredValue works similarly, but with a slightly different approach. Instead of wrapping a callback, you pass the value you want to defer directly to the hook. React schedules the update at the optimal time for performance. This is handy for things like list filtering as well, and—bonus—you don’t have to manage any pending state yourself!

Miscellaneous Hooks: useDebugValue and useId

useDebugValue is mainly for library and advanced custom hook authors, especially if you use React DevTools. By labeling your hooks with a string, you make debugging easier and faster when tracking down custom hook usage.

useId does exactly what the name implies: generates a unique ID for each call. A common use case is giving form inputs and their labels matching, unique IDs—especially important when those components might appear multiple times on one page. For instance, if you reuse an <EmailInput /> component twice in a form, useId ensures each has a collision-proof ID.

Pro tip: Don’t use useId for React list keys. It’s designed for IDs needed at the DOM level, not for uniquely identifying React list items.

New and Notable: React 19 Hooks

React 19 introduces even more powerful hooks and patterns. If you’re curious about what’s new and want to master the latest additions, check out the in-depth video linked at ReactBootcamp.dev, where you’ll find a dedicated walkthrough on all the new React 19 hooks—what they do, and how to start using them today.

Level Up Your React Skills!

We’ve covered every major hook in React, what it does, and when you’ll want to use it. If you’re itching to master every nuance, build real-world projects, and tackle a mountain of hands-on challenges, the React Bootcamp course is an excellent place to dive in. Expect interactive problems, fun video lessons, and comprehensive cheat sheets covering all these hooks and more!

🎥 Want to go deeper into the latest hooks? Click here to watch the full React 19 video guide. Thanks for reading—and happy coding!

Recommended Articles

Code Report