Introduction
Most React developers are comfortable with useState and useEffect, but React offers a rich set of hooks that can dramatically improve your code quality and performance.
useReducer: Complex State Logic
When state logic gets complex, useReducer is your friend:
javascriptconst initialState = { count: 0, step: 1 }; function reducer(state, action) { switch (action.type) { case 'increment': return { ...state, count: state.count + state.step }; case 'decrement': return { ...state, count: state.count - state.step }; case 'setStep': return { ...state, step: action.payload }; default: throw new Error('Unknown action'); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}> +{state.step} </button> </div> ); }
useMemo: Expensive Calculations
Avoid recalculating expensive values on every render:
javascriptfunction ProductList({ products, filter }) { const filteredProducts = useMemo(() => { console.log('Filtering products...'); return products.filter(p => p.name.toLowerCase().includes(filter.toLowerCase()) ); }, [products, filter]); return ( <ul> {filteredProducts.map(p => <li key={p.id}>{p.name}</li>)} </ul> ); }
useCallback: Stable Function References
Prevent unnecessary child re-renders:
javascriptfunction Parent() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { setCount(c => c + 1); }, []); return <MemoizedChild onClick={handleClick} />; }
Custom Hooks: Reusable Logic
Extract and reuse stateful logic:
javascriptfunction useLocalStorage(key, initialValue) { const [value, setValue] = useState(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch { return initialValue; } }); useEffect(() => { window.localStorage.setItem(key, JSON.stringify(value)); }, [key, value]); return [value, setValue]; } // Usage function App() { const [theme, setTheme] = useLocalStorage('theme', 'dark'); }
useRef: Beyond DOM References
useRef can store any mutable value:
javascriptfunction Timer() { const intervalRef = useRef(null); const [seconds, setSeconds] = useState(0); const start = () => { intervalRef.current = setInterval(() => { setSeconds(s => s + 1); }, 1000); }; const stop = () => clearInterval(intervalRef.current); return ( <div> <p>{seconds}s</p> <button onClick={start}>Start</button> <button onClick={stop}>Stop</button> </div> ); }
When to Use What?
| Hook | Use Case |
|---|---|
useState | Simple state values |
useReducer | Complex state with multiple sub-values |
useMemo | Expensive calculations |
useCallback | Stable function refs for child components |
useRef | Mutable values, DOM refs |
| Custom Hooks | Shared stateful logic |
Conclusion
Mastering React hooks beyond the basics will make your code more performant, maintainable, and elegant. Start by identifying patterns in your code that could benefit from useReducer or custom hooks, and refactor gradually.
