Skip to content

React Hooks Reference

Fresh

Hooks let you use state and other React features from function components.

State Hooks

useState

Adds a state variable to your component.

jsx
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  // count: current value
  // setCount: updater function
  // 0: initial value
}

Parameters:

  • initialState - The value you want the state to be initially. Can be a value or an initializer function.

Returns: [state, setState]

  • state - The current state value
  • setState - The set function to update state and trigger re-render

Caveats:

  • setState does not change state in the already-executing code (state is a snapshot)
  • If the new value is identical to current (by Object.is), React skips re-rendering
  • React batches state updates and flushes them together

Updater functions:

jsx
// Replace with value
setCount(5);

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

Lazy initialization:

jsx
// Function is called only on first render
const [todos, setTodos] = useState(() => createInitialTodos());

useReducer

Manages state with a reducer function.

jsx
import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw Error('Unknown action: ' + action.type);
  }
}

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

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}

Parameters:

  • reducer(state, action) - Pure function that takes current state and action, returns next state
  • initialArg - Value from which the initial state is calculated
  • init? - Optional initializer function. Initial state is init(initialArg) if provided, else initialArg

Context Hooks

useContext

Reads and subscribes to a context.

jsx
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext.js';

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

useContext always looks for the closest provider above the component that calls it. It searches upward and does not consider providers in the component from which you're calling useContext.

Ref Hooks

useRef

Declares a ref (a mutable value that doesn't trigger re-renders).

jsx
import { useRef } from 'react';

function Stopwatch() {
  const intervalRef = useRef(0);     // store a timer ID
  const inputRef = useRef(null);     // store a DOM node

  function handleClick() {
    inputRef.current.focus();
  }
}

Parameters:

  • initialValue - The value for ref.current initially

Returns: An object with a single current property, initially set to initialValue

WARNING

Do not read or write ref.current during rendering. Read/write it in event handlers or effects.

useImperativeHandle

Customizes the ref exposed to parent components.

jsx
import { forwardRef, useImperativeHandle, useRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    focus() {
      inputRef.current.focus();
    },
    scrollIntoView() {
      inputRef.current.scrollIntoView();
    },
  }), []);

  return <input {...props} ref={inputRef} />;
});

Effect Hooks

useEffect

Connects a component to an external system.

jsx
import { useEffect } from 'react';

function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();
    return () => connection.disconnect(); // cleanup
  }, [roomId]); // dependency array
}

Dependency array behavior:

  • useEffect(() => {...}) - Runs after every render
  • useEffect(() => {...}, []) - Runs only on mount
  • useEffect(() => {...}, [a, b]) - Runs on mount and when a or b change

useLayoutEffect

Fires before the browser repaints the screen. Use for measuring layout:

jsx
import { useLayoutEffect, useRef, useState } from 'react';

function Tooltip({ children, targetRect }) {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0);

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height);
  }, []);

  // Position tooltip based on measured height
}

Performance

useLayoutEffect blocks the browser from repainting. Prefer useEffect when possible.

useInsertionEffect

For CSS-in-JS library authors. Fires before any DOM mutations. Don't use in application code.

Performance Hooks

useMemo

Caches the result of a calculation between re-renders.

jsx
import { useMemo } from 'react';

function TodoList({ todos, filter }) {
  const visibleTodos = useMemo(
    () => filterTodos(todos, filter),
    [todos, filter]
  );
}

Only use useMemo for expensive calculations. Profile first.

useCallback

Caches a function definition between re-renders.

jsx
import { useCallback } from 'react';

function ProductPage({ productId }) {
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', { referrer, orderDetails });
  }, [productId, referrer]);

  return <ShippingForm onSubmit={handleSubmit} />;
}

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

useTransition

Updates state without blocking the UI.

jsx
import { useState, useTransition } from 'react';

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }

  return (
    <>
      <TabButton onClick={() => selectTab('about')}>About</TabButton>
      <TabButton onClick={() => selectTab('posts')}>Posts</TabButton>
      {isPending && <Spinner />}
      <TabPanel tab={tab} />
    </>
  );
}

useDeferredValue

Defers updating a part of the UI.

jsx
import { useState, useDeferredValue } from 'react';

function SearchPage() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  // query updates immediately (for the input)
  // deferredQuery updates with a delay (for the results)
}

Other Hooks

useId

Generates unique IDs for accessibility attributes.

jsx
import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <input type="password" aria-describedby={passwordHintId} />
      <p id={passwordHintId}>Must contain at least 8 characters</p>
    </>
  );
}

DANGER

Don't use useId to generate keys in a list. Keys should come from your data.

use

Reads the value of a resource like a Promise or context.

jsx
import { use } from 'react';

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);
  return comments.map(comment => <p key={comment.id}>{comment.text}</p>);
}

Unlike other hooks, use can be called inside conditions and loops.

useOptimistic

Shows a different state while an async action is underway.

jsx
import { useOptimistic } from 'react';

function Thread({ messages, sendMessage }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [...state, { text: newMessage, sending: true }]
  );

  async function formAction(formData) {
    const message = formData.get('message');
    addOptimisticMessage(message);
    await sendMessage(message);
  }
}

useActionState

Manages state for form actions.

jsx
import { useActionState } from 'react';

async function increment(previousState, formData) {
  return previousState + 1;
}

function StatefulForm() {
  const [state, formAction, isPending] = useActionState(increment, 0);
  return (
    <form action={formAction}>
      {state}
      <button disabled={isPending}>Increment</button>
    </form>
  );
}