Land Your Dream Job
AI-Powered Resume Builder
Create an ATS-friendly resume in minutes. Free forever!
5 min to read
React Hooks have revolutionized modern frontend development. By simplifying state management, side effects, and reusable logic, Hooks enable developers to write cleaner, more modular, and maintainable components.
In this guide, we explore the best 20 React Hooks—including both built-in and custom hooks—that power the vast majority of React applications today.
useState
The useState
hook adds local state to functional components.
Use cases: Form inputs, toggles, counters, local UI state.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}
Why it matters: Most interactive components depend on useState
for internal logic.
useEffect
Handles side effects such as data fetching, event listeners, and DOM manipulation.
Use cases: API calls, subscriptions, timers.
import { useEffect } from 'react';
useEffect(() => {
document.title = "React App";
return () => {
// Cleanup
};
}, []);
Why it matters: Connects components to the outside world—essential for real apps.
useRef
Creates a mutable reference that persists across renders without causing re-renders.
Use cases: Accessing DOM nodes, timers, storing previous state.
import { useRef } from 'react';
function InputFocus() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>
</>
);
}
Why it matters: Bridges the gap between React and the DOM.
useCallback
Returns a memoized version of a function to avoid re-creating it on every render.
Use cases: Stable event handlers, performance optimizations.
import { useCallback } from 'react';
const handleClick = useCallback(() => {
// Expensive operation
}, []);
Why it matters: Prevents unnecessary re-renders in child components.
useMemo
Memoizes the result of an expensive computation.
Use cases: Derived state, filtering, performance tuning.
import { useMemo } from 'react';
const filteredList = useMemo(() => {
return list.filter(item => item.active);
}, [list]);
Why it matters: Boosts performance by avoiding redundant calculations.
useContext
Accesses values from React’s Context API to avoid prop drilling.
Use cases: Themes, auth, localization.
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>Click me</button>;
}
Why it matters: Simplifies global state access.
useId
Generates a unique ID for accessibility and hydration consistency.
Use cases: Label/input associations, SSR-safe IDs.
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
return (
<>
<input aria-describedby={passwordHintId} />
<div id={passwordHintId}>Password must be at least 8 characters.</div>
</>
);
}
Why it matters: Prevents ID collisions across renders and servers.
useReducer
Alternative to useState
for complex or interdependent state logic.
Use cases: Forms, state machines, toggles with logic.
import { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<button onClick={() => dispatch({ type: 'increment' })}>
Count: {state.count}
</button>
);
}
Why it matters: Enables structured, testable state transitions.
useImperativeHandle
Customizes values exposed to parent components using ref
.
Use cases: Exposing methods like focus()
from child to parent.
import { useImperativeHandle, forwardRef, useRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus()
}));
return <input ref={inputRef} />;
});
Why it matters: Enables parent-child communication via imperative APIs.
useLayoutEffect
Runs synchronously after DOM mutations and before painting.
Use cases: DOM measurements, animations.
import { useLayoutEffect, useRef } from 'react';
function LayoutComponent() {
const divRef = useRef();
useLayoutEffect(() => {
// Measure layout
}, []);
return <div ref={divRef}>Content</div>;
}
Why it matters: Useful for precise layout control.
useDebugValue
Displays custom hook values in React DevTools.
Use cases: Debugging custom hooks.
import { useDebugValue } from 'react';
function useFriendStatus(friendID) {
const isOnline = useFriendStatusAPI(friendID);
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
Why it matters: Improves transparency for debugging.
useDeferredValue
Defers non-urgent state updates for smoother UI.
Use cases: Search inputs, expensive re-renders.
import { useDeferredValue } from 'react';
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
// Render based on deferredQuery
}
Why it matters: Enhances perceived performance.
useTransition
Marks state updates as non-urgent to avoid blocking interactions.
Use cases: Large UI updates, pagination.
import { useTransition } from 'react';
function App() {
const [isPending, startTransition] = useTransition();
const handleClick = () => {
startTransition(() => {
// Trigger heavy update
});
};
return <button onClick={handleClick}>Update</button>;
}
Why it matters: Keeps the UI responsive during updates.
useSyncExternalStore
Subscribes to external data sources in a concurrent-safe way.
Use cases: Redux-like stores, subscriptions.
import { useSyncExternalStore } from 'react';
function useExternalStore(store) {
return useSyncExternalStore(
store.subscribe,
store.getSnapshot,
store.getServerSnapshot
);
}
Why it matters: Ensures consistency with concurrent rendering.
useInsertionEffect
Injects styles before DOM changes. Ideal for CSS-in-JS libraries.
Use cases: Runtime style injection.
import { useInsertionEffect } from 'react';
function StyleInjector() {
useInsertionEffect(() => {
// Inject CSS here
}, []);
return null;
}
Why it matters: Prevents layout shifts from dynamic styles.
useBoolean
(Custom)Simplifies boolean state toggles.
Use cases: Modal visibility, switches.
function useBoolean(initialValue = false) {
const [value, setValue] = useState(initialValue);
const setTrue = () => setValue(true);
const setFalse = () => setValue(false);
const toggle = () => setValue(v => !v);
return [value, { setTrue, setFalse, toggle }];
}
Why it matters: Reduces boilerplate.
usePrevious
(Custom)Tracks the previous value of a prop or state.
Use cases: Comparing values, tracking changes.
import { useRef, useEffect } from 'react';
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
Why it matters: Adds history to stateful logic.
useFetch
(Custom)Simplifies data fetching logic into a reusable hook.
Use cases: API calls, data loading.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
Why it matters: Encapsulates fetching logic into clean reusable code.
useEventListener
(Custom)Attaches and cleans up event listeners.
Use cases: Resize events, keyboard shortcuts.
import { useEffect } from 'react';
function useEventListener(event, handler, element = window) {
useEffect(() => {
element.addEventListener(event, handler);
return () => element.removeEventListener(event, handler);
}, [event, handler, element]);
}
Why it matters: Ensures proper cleanup and reattachment.
useLocalStorage
(Custom)Synchronizes state with localStorage.
Use cases: Persisting preferences, tokens.
import { useState } from 'react';
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = value => {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
};
return [storedValue, setValue];
}
Why it matters: Adds persistence with minimal effort.
Hook | Use Case | Common Example |
---|---|---|
useState |
Local state | Counters, toggles |
useEffect |
Side effects | Fetching data |
useRef |
Mutable reference, DOM access | Input focus, timers |
useCallback |
Stable function reference | Event handlers |
useMemo |
Memoized computation | Filtered lists |
useContext |
Global/shared state | Themes, auth contexts |
useId |
Unique ID generation | Form fields, SSR |
useReducer |
Complex state transitions | Form logic |
useImperativeHandle |
Expose methods via ref |
Trigger methods on children |
useLayoutEffect |
Synchronous layout effects | Animations, DOM reads |
Mastering these 20 React Hooks—both core and custom—will dramatically improve how you structure and scale your React applications. They cover 90% of the real-world use cases in modern component design, offering performance, clarity, and reusability out of the box.
Need expert guidance? Connect with a top Codersera professional today!