Ever wondered why your child components keep re-rendering even when their props haven't changed? Or why performance drops in large React apps? The answer might lie in one powerful but underused hook β useCallback.
In this blog post, weβll explore Reactβs useCallback hook in depth. Youβll learn how it works, when to use it, and how to pair it with tools like React.memo, useState, and useMemo for real-world performance optimization.
π What is useCallback in React?
useCallback is a React Hook that returns a memoized version of a callback function. It prevents the function from being re-created on every render β unless the dependencies change.
const memoizedFn = useCallback(() => { doSomething(a, b); }, [a, b]);
This becomes crucial when you're passing callbacks to memoized child components (React.memo
), which rely on reference equality to avoid re-rendering.
π§ Real-World Analogy
Think of your callback as a phone number. If it changes every time, the person you're calling (your child component) thinks it's someone new β and answers again. useCallback keeps the number the same unless there's a real reason to change.
β Why Use useCallback?
- Prevent unnecessary re-renders
- Improve app performance
- Stabilize function references passed as props
- Avoid expensive recalculations
β Without useCallback
const Parent = () => { const [count, setCount] = useState(0); const handleClick = () => { console.log("Clicked"); }; return ( <> <button onClick={() => setCount(count + 1)}>Increment</button> <Child onClick={handleClick} /> </> ); }; const Child = React.memo(({ onClick }) => { console.log("Child re-rendered"); return <button onClick={onClick}>Click me</button>; });
Here, Child
re-renders even when it doesnβt need to β because handleClick
is recreated on every render.
β
With useCallback
const handleClick = useCallback(() => { console.log("Clicked"); }, []);
Now the Child
component wonβt re-render unnecessarily, thanks to the stable function reference.
π§ͺ useCallback
vs useMemo
| Hook | Purpose | Returns | | ------------- | ----------------------- | ----------------------- | | `useCallback` | Memoizes a **function** | Same function reference | | `useMemo` | Memoizes a **value** | Result of computation |
const memoizedCallback = useCallback(() => compute(a, b), [a, b]); const memoizedValue = useMemo(() => compute(a, b), [a, b]);
π Debug With React DevTools
Use the Profiler in React DevTools to:
- Inspect render frequency
- Validate optimizations
- Track function reference changes
βοΈ Best Practices
β
Use with React.memo
for child components
β
Use when function dependencies change infrequently
β
Avoid overuse β it has memory overhead
β
Always pass accurate dependencies
β
Combine with useMemo
when needed
π‘ Bonus: Working with useState
const [count, setCount] = useState(0); const increment = useCallback(() => { setCount(prev => prev + 1); }, []);
π¬ Let's Connect
Have questions or want to share how you use useCallback? Drop a comment below or explore more tips on my blog:
π Mastering useCallback in React
Top comments (0)