Skip to content

Hooks

Say a component needs to remember whether a dropdown is open, or needs to run some code after it renders to sync with a server. Both are jobs for a hook. You've already used two of them: useState gives a component memory, and useEffect runs code after a render to reach outside React. Those are the two you'll use most, and the rest of the built-ins follow the same shape: you call a function inside your component, and it wires you into part of React's machinery. The name of that function always starts with use, and that prefix is the whole definition of a hook.

A tour of the common built-ins

You don't need every hook on day one, but it helps to know what's on the shelf so you recognize the right tool when a problem calls for it. Here are the ones that come up most:

  • useRef holds a mutable value that survives renders without causing one. It's also how you get a direct reference to a DOM element when you need one, like focusing an input.
  • useContext reads shared data from a surrounding provider, so a deeply nested component can reach a value without you passing it down through every level in between.
  • useReducer manages state through a reducer function, where each update describes a transition from the current state to the next. It's a good fit when state changes get complex enough that a single useState starts to strain.
  • useMemo caches a computed value between renders and only recalculates it when its inputs change.
  • useCallback does the same for a function, handing you back the same function instance between renders instead of a fresh one each time.

useMemo and useCallback get more room later, once there's a real performance story to tell. Refs and the DOM picks up useRef in full. For useContext and useReducer, this chapter is the deepest treatment they get, so come back here when you need a refresher. For now, the point is the pattern: a hook is your way into a specific React feature, and its name tells you it's a hook.

The rules of hooks

Hooks come with two rules, and React counts on you following them.

First, call hooks at the top level of your component. Don't call them inside a condition, a loop, or a nested function. Every hook a component uses should run on every render, in the same order, every time.

jsx
function Profile({ isLoggedIn }) {
  // Wrong: this hook only runs sometimes
  if (isLoggedIn) {
    const [name, setName] = useState('')
  }

  // Right: call it at the top level, unconditionally
  const [name, setName] = useState('')
  // ...then use isLoggedIn inside your logic
}

Second, only call hooks from React function components or from other custom hooks. Regular JavaScript functions don't get to use hooks, because there's no component render for React to attach the state to.

Writing your own hooks

Say several components need to know the width of the browser window. Instead of copying the same useState and useEffect wiring into each one, you can package that logic once:

jsx
import { useState, useEffect } from 'react'

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth)

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth)
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])

  return width
}

Now any component can call const width = useWindowWidth() and get a value that updates as the window resizes. useWindowWidth is a custom hook: a function that calls other hooks. That's the whole pattern, and because it's a function, you can give a piece of stateful logic a name, put it in one place, and reuse it across components. The use prefix is what marks it as a hook, both for React's tooling and for the next person reading your code. Custom hooks bundle the hooks you already have into something reusable; they don't add new powers to React.

A few rules of thumb for reaching into that list. Reach for useRef when you need to hold onto a value, like a timer ID or a previous prop, that shouldn't trigger a re-render when it changes, or when you need to grab a DOM node directly. Reach for useContext when a value like the current theme or the logged-in user needs to reach several layers of nested components, and threading it through props at every level would mean touching components that don't otherwise care about it. Reach for useReducer when a single piece of state has several sub-values that update together, or when the next state depends on a specific action rather than a plain new value, since a reducer keeps that transition logic in one function instead of scattered across several setState calls. Reach for useMemo or useCallback only after you've noticed an actual slowdown, most often an expensive calculation re-running on every render, or a function identity that's breaking memoization somewhere else, like a React.memo'd child or another hook's dependency array.

Naming and extracting a custom hook follows a pattern of its own. The name always starts with use, so both React's tooling and anyone reading the code can tell it's allowed to call other hooks. When you notice the same combination of useState and useEffect, or any other hooks, showing up in more than one component, that's the signal to extract: pull the logic into its own use-prefixed function, have it return whatever the components actually need, a value, a setter, or a pair of both, and call that function from each component instead of repeating the wiring.

The rules aren't arbitrary. React doesn't know the names of your state variables; it tracks the hooks in a component by the order they're called. First hook, second hook, third hook, and so on. On the next render it walks that same list in the same order and matches each call back to the state it set up last time. Keep the order identical on every render and the matching holds. Put a hook behind a condition and the list shifts the moment that condition flips, so React lines up the wrong state with the wrong call and the component breaks in confusing ways. Calling at the top level, unconditionally, is what guarantees a stable order.

useMemo and useCallback are worth a separate note. They're manual optimizations: you're telling React to hold onto a computed value or a function so it doesn't have to redo the work or hand out a new instance on every render. They cost you some complexity, and it's tempting to reach for them where they don't actually help. The React Compiler increasingly makes them unnecessary by memoizing this kind of thing for you at build time, so treat reaching for them by hand as something you do deliberately, where you've measured a real cost, rather than a reflex on every value.

JunoHooks let a component use React features A hook is a function whose name starts with use, and it plugs your component into a React feature like memory or effects. You've already met useState and useEffect, and the others follow the same shape. The main thing to remember: call hooks at the top of your component, never inside an if or a loop, so they run the same way every render.
JunoHooks let a component use React features Beyond useState and useEffect, you've got useRef for values that survive renders without triggering one, useContext to skip prop drilling, useReducer for structured state transitions, and useMemo/useCallback for caching. Call them at the top level of a component or another hook, always in the same order. When you find yourself repeating stateful logic, pull it into a custom hook, a use function that calls other hooks, and reuse it.
JunoHooks let a component use React features React matches hooks to their state by call order, not by name, so the order has to be identical on every render. That single fact is where both rules come from. Custom hooks are composition: a function calling hooks, which is why the same ordering rules apply inside them. And treat useMemo/useCallback as deliberate, measured optimizations, since the React Compiler is steadily making the hand-written versions unnecessary.

Next up: Thinking in React, where components, props, state, and effects come together into a process for turning a design into a UI.