Click any hook to expand the explanation and examples.
π State Hooks
useState essential
const [count, setCount] = useState(0); const [user, setUser] = useState(null); const [items, setItems] = useState([]);Always use the callback form (// Update with new value setCount(5);
// Update based on previous state setCount(prev => prev + 1);
// Lazy initialization (expensive computation) const [data, setData] = useState(() => computeExpensiveValue());
prev => β¦) when the new state depends on the old state.
useReducer essential
function reducer(state, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'decrement': return { count: state.count - 1 };
case 'reset': return { count: 0 };
default: throw new Error('Unknown action');
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: βincrementβ });
dispatch({ type: βresetβ });
Prefer useReducer over useState when you have multiple related state values or complex update logic.
β‘ Effect Hooks
useEffect essential
// Run on every render
useEffect(() => {
console.log('rendered');
});
// Run once on mount
useEffect(() => {
fetchData();
}, []);
// Run when dependency changes
useEffect(() => {
fetchUser(userId);
}, [userId]);
// Cleanup (runs before next effect and on unmount)
useEffect(() => {
const sub = subscribe(channel);
return () => sub.unsubscribe();
}, [channel]);
Always include all values from the component scope that change over time in the dependency array.
useLayoutEffect advanced
useEffect, but fires synchronously after DOM mutations, before the browser paints.
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setHeight(height);
}, []);
Use this only when you need to read layout from the DOM and re-render synchronously before the user sees the update. Prefer useEffect in most cases.
π Context
useContext essential
const ThemeContext = createContext('light');
// Provider (parent)
<ThemeContext.Provider value=βdarkβ>
<App />
</ThemeContext.Provider>
// Consumer (any child)
function Button() {
const theme = useContext(ThemeContext);
return <button className={theme}>Click</button>;
}
The component re-renders whenever the context value changes.
π Ref Hooks
useRef essential
// DOM reference
const inputRef = useRef(null);
<input ref={inputRef} />
inputRef.current.focus();
// Mutable value (like instance variable)
const intervalId = useRef(null);
intervalId.current = setInterval(tick, 1000);
clearInterval(intervalId.current);
// Previous value pattern
const prevCount = useRef(count);
useEffect(() => {
prevCount.current = count;
});
Changing .current does not trigger a re-render.
useImperativeHandle advanced
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ''; }
}));
return <input ref={inputRef} />;
});
// Parent can now call
ref.current.focus();
ref.current.clear();
Use sparingly β prefer passing props over imperative handles.
π Performance Hooks
useMemo performance
const sortedList = useMemo(() => {
return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
const filteredUsers = useMemo(() => {
return users.filter(u => u.active);
}, [users]);
Only use when you have a measurably expensive calculation. Donβt wrap everything in useMemo.
useCallback performance
const handleClick = useCallback((id) => {
setItems(prev => prev.filter(item => item.id !== id));
}, []);
// Useful when passing callbacks to memoized children
<MemoizedChild onClick={handleClick} />
useCallback(fn, deps) is equivalent to useMemo(() => fn, deps). Mainly useful when passing callbacks to components wrapped in React.memo.
π React 18+ Hooks
useId react18
function FormField({ label }) {
const id = useId();
return (
<>
<label htmlFor={id}>{label}</label>
<input id={id} />
</>
);
}
Don't use useId for keys in a list β use your data's unique identifier instead.
useTransition react18
const [isPending, startTransition] = useTransition();The input stays responsive while the expensive list filtering happens in the background.function handleSearch(query) { startTransition(() => { setSearchResults(filterLargeList(query)); }); }
return ( <> <input onChange={e => handleSearch(e.target.value)} /> {isPending ? <Spinner /> : <Results data={searchResults} />} </> );
useDeferredValue react18
function Search({ query }) {
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<div style={{ opacity: isStale ? 0.5 : 1 }}>
<HeavyList query={deferredQuery} />
</div>
);
}
Similar to useTransition but for values you receive as props rather than state you control.
useSyncExternalStore react18
const width = useSyncExternalStore(
// subscribe
(callback) => {
window.addEventListener('resize', callback);
return () => window.removeEventListener('resize', callback);
},
// getSnapshot (client)
() => window.innerWidth,
// getServerSnapshot (SSR)
() => 1024
);
Primarily used by state management libraries (Redux, Zustand) to integrate with React's concurrent features.
π οΈ Custom Hooks
useLocalStorage pattern
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
// Usage
const [theme, setTheme] = useLocalStorage(βthemeβ, βlightβ);
useFetch pattern
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
fetch(url, { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// Usage
const { data, loading, error } = useFetch(β/api/usersβ);
useMediaQuery pattern
function useMediaQuery(query) {
const [matches, setMatches] = useState(
() => window.matchMedia(query).matches
);
useEffect(() => {
const mql = window.matchMedia(query);
const handler = (e) => setMatches(e.matches);
mql.addEventListener('change', handler);
return () => mql.removeEventListener('change', handler);
}, [query]);
return matches;
}
// Usage
const isMobile = useMediaQuery(β(max-width: 768px)β);
useDebounce pattern
function useDebounce(value, delay = 300) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
// Usage
const [query, setQuery] = useState(β);
const debouncedQuery = useDebounce(query, 500);
useEffect(() => { search(debouncedQuery); }, [debouncedQuery]);
π Rules of Hooks
Rule 1: Only call at the top level rules
// β Wrong
if (loggedIn) {
useEffect(() => { ... });
}
// β
Correct
useEffect(() => {
if (loggedIn) { β¦ }
}, [loggedIn]);
React relies on the order hooks are called to match state to the correct hook.
Rule 2: Only call from React functions rules
// β Wrong β regular function
function getUser() {
const [user, setUser] = useState(null);
}
// β
Correct β component
function UserProfile() {
const [user, setUser] = useState(null);
}
// β
Correct β custom hook (starts with βuseβ)
function useUser() {
const [user, setUser] = useState(null);
return user;
}
Custom hooks must start with use so React can check for rule violations.
Rule 3: Exhaustive dependencies rules
// β Missing dependency
useEffect(() => {
fetchUser(userId);
}, []);
// β
Correct
useEffect(() => {
fetchUser(userId);
}, [userId]);
Use the eslint-plugin-react-hooks plugin β it catches these automatically.
Quick Reference Table
| Hook | Purpose |
|---|---|
useState | Simple state |
useReducer | Complex state logic |
useEffect | Side effects (fetch, subscribe) |
useLayoutEffect | Synchronous DOM reads |
useContext | Read context values |
useRef | DOM refs / mutable values |
useMemo | Cache expensive computations |
useCallback | Cache function references |
useId | Generate unique IDs |
useTransition | Non-urgent state updates |
useDeferredValue | Defer re-rendering |
useSyncExternalStore | Subscribe to external stores |
useImperativeHandle | Customize exposed ref |