On this page
React Hooks Overview
Hooks let function components use state, lifecycle, and other React features without classes.
Rules of Hooks
- Only call hooks at the top level — not inside loops, conditions, or nested functions
- Only call hooks from React function components or custom hooks
// WRONG
if (condition) {
const [x, setX] = useState(0); // Violates rules
}
// CORRECT
const [x, setX] = useState(0);
if (condition) { /* use x */ }
Built-in Hooks Reference
| Hook | Purpose |
|---|---|
useState |
Component state |
useEffect |
Side effects |
useContext |
Consume context |
useRef |
Mutable ref, DOM access |
useMemo |
Memoize computed values |
useCallback |
Memoize functions |
useReducer |
Complex state logic |
useRef
Persist values across renders without causing re-renders:
import { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>
</div>
);
}
// Store previous value
function usePrevious(value) {
const ref = useRef();
useEffect(() => { ref.current = value; });
return ref.current;
}
useMemo
Cache expensive calculations:
import { useMemo } from 'react';
function ProductList({ products, filter }) {
const filtered = useMemo(() => {
return products.filter(p => p.category === filter);
}, [products, filter]); // Recompute only when deps change
return filtered.map(p => <ProductCard key={p.id} product={p} />);
}
useCallback
Cache function references (useful with memoized children):
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // Stable reference
return <MemoizedChild onClick={handleClick} />;
}
useReducer
For complex state logic:
import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'decrement': return { count: state.count - 1 };
case 'reset': return initialState;
default: return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
Custom Hooks
Extract reusable logic:
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
// Usage
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
Toggle theme: {theme}
</button>
);
}
Custom hooks must start with use. They share the same rules as built-in hooks.