ReactIntroduction to Hooks

Introduction to Hooks

Before React 16.8, any component that needed state or lifecycle behaviour had to be written as a class. Function components were "stateless" — pure UI renderers. Hooks, introduced in React 16.8, changed everything. They let you use state, side effects, context, and more inside ordinary function components.

What Is a Hook?

A hook is a JavaScript function whose name starts with use that lets a function component tap into React features. Hooks do not work inside classes — they are the function-component alternative to class lifecycle methods and instance variables.

JSX
// 'useState' is a hook — it hooks your component into React's state system
import { useState } from 'react'

function Counter() {
  const [count, setCount] = useState(0)   // ← hook call
  return <button onClick={() => setCount(count + 1)}>Count: {count}</button>
}
Why Hooks? The Problem They Solve

Class components had three persistent pain points that hooks directly address:

  • Reusing stateful logic was hard. Patterns like Higher-Order Components and render props worked but wrapped components in layers of nesting (wrapper hell). Custom hooks let you extract and reuse stateful logic without changing the component tree.

  • Complex components became hard to understand. Related code was split across multiple lifecycle methods (componentDidMount, componentDidUpdate, componentWillUnmount), and unrelated code was mixed together in each. Hooks let you split code by concern, not by lifecycle method.

  • Classes were confusing. Binding this, understanding when to use arrow functions vs regular methods, and the subtle differences between lifecycle methods all created friction. Function components with hooks are just functions.

Before Hooks: A Class Component

JSX
// React before 16.8 — required a class for stateful behaviour
class WindowWidth extends React.Component {
  constructor(props) {
    super(props)
    this.state = { width: window.innerWidth }
    this.handleResize = this.handleResize.bind(this)
  }

  componentDidMount() {
    window.addEventListener('resize', this.handleResize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize)
  }

  handleResize() {
    this.setState({ width: window.innerWidth })
  }

  render() {
    return <p>Window width: {this.state.width}px</p>
  }
}
After Hooks: The Same Logic as a Function Component

JSX
import { useState, useEffect } from 'react'

// Identical behaviour, half the code, no class machinery
function WindowWidth() {
  const [width, setWidth] = useState(window.innerWidth)

  useEffect(() => {
    function handleResize() {
      setWidth(window.innerWidth)
    }
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)  // cleanup
  }, [])   // [] means run once on mount

  return <p>Window width: {width}px</p>
}

And the real power of hooks: you can extract the resize logic into a reusable custom hook and share it across any component:

JSX
// hooks/useWindowWidth.js
import { useState, useEffect } from 'react'

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

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

  return width
}

// Now any component can use it in one line:
function Sidebar() {
  const width = useWindowWidth()
  return width < 768 ? <MobileMenu /> : <DesktopMenu />
}
Built-in Hooks at a Glance

Hook

Purpose

useState

Add local state to a function component

useEffect

Run side effects (fetch, subscribe, timers) after render

useContext

Read a Context value without a Consumer wrapper

useReducer

Manage complex state with a reducer function (like Redux locally)

useRef

Hold a mutable value or reference a DOM node without triggering re-renders

useMemo

Cache an expensive computed value between renders

useCallback

Cache a function reference between renders

useId

Generate a stable unique ID for accessibility attributes

useTransition

Mark a state update as non-urgent to keep the UI responsive

useDeferredValue

Defer re-rendering a slow part of the UI

useSyncExternalStore

Subscribe to external (non-React) stores safely

useLayoutEffect

Like useEffect but fires synchronously after DOM mutations

useDebugValue

Add a label to a custom hook in React DevTools

Custom Hooks

Any function that calls other hooks and starts with use is a custom hook. Custom hooks are the primary mechanism for sharing stateful logic between components — no class mixins, no HOC wrappers required.

JSX
// A custom hook that manages a boolean toggle
function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue)
  const toggle = () => setValue(v => !v)
  return [value, toggle]
}

// Usage
function Accordion({ title, children }) {
  const [isOpen, toggleOpen] = useToggle(false)

  return (
    <div>
      <button onClick={toggleOpen}>{isOpen ? '▾' : '▸'} {title}</button>
      {isOpen && <div>{children}</div>}
    </div>
  )
}
Note
Custom hooks are not a React feature — they are just a naming convention. React DevTools and the `eslint-plugin-react-hooks` lint rules use the `use` prefix to identify hooks and enforce the rules of hooks on them.
Hooks Do Not Replace Everything

A few scenarios still require class components:

  • Error Boundaries — the componentDidCatch and getDerivedStateFromError lifecycle methods have no hook equivalent. You still write a class component as an error boundary wrapper.

  • Legacy codebases — existing class components do not need to be rewritten. Hooks and class components coexist in the same app.

  • Some very old lifecycle methods (getSnapshotBeforeUpdate, UNSAFE_componentWillReceiveProps) also lack hook equivalents — though they are rarely needed.

Tip
If you are starting a new component today, always write a function component with hooks. The React team considers hooks the future of React and is investing all new API design in the hooks model.