Published , Last updated
React Hooks, introduced in React 16.8, allow you to use state, lifecycle, context, refs, and advanced features directly in function components - no classes needed. Below, you'll find them categorized by purpose, with explanations and code examples.
Declare and update component state:
const [count, setCount] = useState(0);
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
Simple and powerful for most cases.
Better for complex state logic:
function reducer(state, action) {
switch (action.type) {
case 'increment': return {count: state.count + 1};
case 'decrement': return {count: state.count - 1};
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, {count: 0});
<button onClick={() => dispatch({type: 'increment'})}>+</button>
Use this over useState
when state transitions are complex.
For side-effects (e.g. data fetch, subscriptions):
useEffect(() => {
const id = setInterval(() => setCount(c => c + 1), 1000);
return () => clearInterval(id);
}, []);
Runs after render; dependencies control re-triggering.
Synchronous effect before browser paint - great for measurements:
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
console.log(rect);
}, []);
Use when you need to read layout or mutate DOM before paint.
Designed for CSS-in-JS libraries to inject styles before DOM updates:
useInsertionEffect(() => {
const style = document.createElement('style');
style.textContent = `.test{color:red}`;
document.head.appendChild(style);
return () => document.head.removeChild(style);
}, []);
Rarely used - only for library authors.
Access React Context easily:
const theme = useContext(ThemeContext);
Prevents props drilling for global values.
Store mutable values (e.g., DOM nodes) without re-rendering:
const inputRef = useRef();
<input ref={inputRef} />;
<button onClick={() => inputRef.current.focus()}>Focus</button>
Excellent for DOM access and timeouts.
Customize what ref exposes in forwardRef:
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus()
}));
Perfect for custom component APIs.
Memoize expensive computations:
const sorted = useMemo(() => items.sort(), [items]);
Avoid rerunning tasks unless dependencies change.
Memoize function identity:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
Prevents unnecessary re-renders in child components.
Display custom hook labels in React DevTools:
useDebugValue(status ? 'Online' : 'Offline');
Improves debugging and readability.
Generate stable unique IDs:
const id = useId();
<label htmlFor={id}>Name</label>
<input id={id} />
Great for SSR and accessibility.
Mark non-urgent updates:
const [isPending, startTransition] = useTransition();
startTransition(() => setList(newList));
Helps keep UI responsive during large updates.
Defer low-priority values:
const deferred = useDeferredValue(filter);
<SlowList text={deferred} />
Optimizes heavy UI rendering.
Safely subscribe to external stores—ideal for global data like online status:
const isOnline = useSyncExternalStore(subscribe, () => navigator.onLine);
Avoids tearing in concurrent mode.
Combine built-in hooks to encapsulate logic:
function useLocalStorage(key, defaultVal) {
const [state, setState] = useState(
() => JSON.parse(localStorage.getItem(key)) ?? defaultVal
);
useEffect(() => {
localStorage.setItem(key, JSON.stringify(state));
}, [key, state]);
return [state, setState];
}
Enhances modularity and reuse.
React Hooks are essential for building modern React apps. From managing state with useState and useReducer, syncing data via useEffect, optimizing via useMemo/useCallback, to advanced concurrent UI flow with useTransition and useDeferredValue, there's a powerful tool for every need. Add in custom hooks to encapsulate and share logic, and you're well-prepared for large-scale, performant React development.