React Hooks Explained: useState, useEffect, useRef, useMemo, useCallback and Custom Hooks With Examples
A practical guide to React hooks: how useState and useEffect work, rules of hooks, useRef, useMemo, useCallback, useReducer, context, custom hooks, common bugs and best practices.
Hooks let function components in React hold state and run side effects. Since their introduction, they have replaced class components in almost all modern React code. Hooks are simple to start with, but a few details, such as dependency arrays and stale values, trip up beginners. This guide explains each important hook with short examples and the mistakes to avoid.
The rules of hooks
- Call hooks only at the top level of a component or custom hook, never inside loops, conditions or nested functions.
- Call hooks only from React function components or other hooks.
- The ESLint plugin
eslint-plugin-react-hookscatches violations and missing dependencies, so enable it.
useState
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Clicked {count} times
</button>
);
}
State updates are asynchronous and batched. When the new value depends on the old one, use the functional form, setCount(c => c + 1). Never mutate state directly: for objects and arrays, create new copies using spread, map or filter.
useEffect
Effects run after render, and are for side effects such as fetching data, subscribing to events or updating the document title.
useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${id}`, { signal: controller.signal })
.then(r => r.json())
.then(setUser)
.catch(() => {});
return () => controller.abort(); // cleanup
}, [id]);
The dependency array controls when the effect runs: empty array means once after the first render, a list means whenever those values change, and omitting it means after every render. Include every value from the component that the effect uses. The cleanup function runs before the effect re-runs and when the component unmounts. Do not use an effect for things that can be calculated during rendering, as that creates extra renders.
useRef
useRef holds a mutable value that persists across renders without causing a re-render. It is commonly used to access DOM elements, for example to focus an input, or to store timers and previous values.
const inputRef = useRef(null);
useEffect(() => inputRef.current?.focus(), []);
return <input ref={inputRef} />;
useMemo and useCallback
useMemo caches an expensive calculated value, and useCallback caches a function so its identity stays stable between renders.
const total = useMemo(() => items.reduce((s, i) => s + i.price, 0), [items]);
const handleSelect = useCallback(id => setSelected(id), []);
Use them only when you measure a real performance problem or when stable references are needed, such as for a memoised child component or an effect dependency. Overusing them adds complexity without benefit.
useReducer
For complex state with several related values or transitions, useReducer keeps updates predictable.
function reducer(state, action) {
switch (action.type) {
case "add": return [...state, action.item];
case "remove": return state.filter(i => i.id !== action.id);
default: return state;
}
}
const [cart, dispatch] = useReducer(reducer, []);
useContext
Context passes data through the component tree without manually threading props, for example a theme or the logged-in user. Keep contexts focused, because every consumer re-renders when the value changes.
Custom hooks
A custom hook is a function starting with use that combines other hooks to reuse logic.
function useLocalStorage(key, initial) {
const [value, setValue] = useState(() => {
try { return JSON.parse(localStorage.getItem(key)) ?? initial; }
catch { return initial; }
});
useEffect(() => localStorage.setItem(key, JSON.stringify(value)), [key, value]);
return [value, setValue];
}
Common bugs
- Infinite loops: an effect that sets state it also depends on, or an object or function created inline and listed as a dependency.
- Stale closures: an effect or callback reads an old value because a dependency is missing.
- Missing keys in lists or using array index as the key when items can reorder.
- Fetching without cleanup, leading to race conditions where an older response overwrites a newer one.
- Updating state after unmount. Use cleanup or abort signals.
Best practices
- Keep components small and focused.
- Lift state up only as far as needed.
- Prefer derived values over duplicated state.
- Use a data-fetching library such as React Query or SWR for server data, which handle caching and errors.
- Test components with React Testing Library.
Learn the underlying language first with our JavaScript basics guide, and explore server-side JavaScript in Node.js for beginners. React's official documentation at react.dev is the best reference, since APIs evolve between versions.
A small exercise to practise
Build a searchable list. Fetch a list of items in a useEffect, store it with useState, keep the search text in another state, and derive the filtered list during rendering instead of storing it. Then extract the fetching into a useFetch custom hook that returns data, loading and error values. Finally, debounce the search input with a small hook using useEffect and a timer cleanup. These three steps cover most patterns you will use in real projects.
Frequently asked questions
What are React hooks?
Functions that let function components use state and other React features.
What is the dependency array in useEffect?
A list of values that determines when the effect re-runs.
When should I use useMemo?
When a calculation is expensive and you have measured a performance issue.
Can I use hooks in class components?
No. Hooks work only in function components and custom hooks.
What is a custom hook?
A function starting with use that reuses stateful logic across components.
Why does my effect run twice in development?
React Strict Mode intentionally runs effects twice in development to help find bugs. It does not do so in production.
Last reviewed 8 November 2026. Rates, limits and rules change, so confirm figures with the official source before you act.
Keep reading
Information on AMTools is for general guidance and is not professional financial, tax, legal or medical advice. Rules, rates and limits change, so check the latest official source before acting.