When to use useCallback()?
When to use useCallback()
The useCallback() hook is needed not to speed up a function,
but to keep the same reference to a function across renders.
This matters when:
- The function is passed to a child component, especially if it is wrapped in
React.memo. - The function is used in
useEffect/useMemoand must stay stable (so it does not trigger the effect again). - You want to avoid unnecessary recreation of functions on every render (which causes unnecessary re-renders).
Example 1: passing a function to a React.memo component
javascript
const Button = React.memo(({ onClick }) => {
console.log("Button re-render");
return <button onClick={onClick}>+</button>;
});
function Counter() {
const [count, setCount] = useState(0);
// Without useCallback → a new function is created on every render
const increment = useCallback(() => setCount(c => c + 1), []);
return (
<div>
<p>{count}</p>
<Button onClick={increment} />
</div>
);
}Here:
incrementis created once, since the dependencies are[].- The button does not re-render without a reason.
Without
useCallback,Buttonwould re-render on every change ofcount.
Example 2: a stable dependency in useEffect
javascript
function FetchUser({ id }) {
const fetchUser = useCallback(() => {
return fetch(`/api/user/${id}`).then(res => res.json());
}, [id]);
useEffect(() => {
fetchUser();
}, [fetchUser]); // useEffect fires only when id changes
}Without useCallback, fetchUser would be recreated every render,
and useEffect would fire every time - even if id had not changed.
Summary - when it is actually worth using
| Situation | Worth using? | Why |
|---|---|---|
Passing a function to a React.memo component | Yes | Avoids unnecessary re-renders |
Using a function in useEffect / useMemo | Yes | Stabilizes the dependency |
| Calling the function directly inside JSX | No | Pointless, it is not preserved |
| The function does not depend on props or state | OK with [] | Created only once |
| A lightweight function that does not affect others | No | The optimization is not worth it |
What happens if you do not specify dependencies
javascript
const memoizedFn = useCallback(() => {
doSomething(a, b);
});Without a dependency array:
-
useCallbackwill recreate the function on every render - meaning no memoization happens at all. -
This is equivalent to simply writing:
javascriptconst memoizedFn = () => doSomething(a, b);
For useCallback to work, you need to pass dependencies
that tell React when the function should actually be updated.
Example with dependencies
javascript
const handleClick = useCallback(() => {
console.log(count);
}, [count]);- The function is recreated only if
countchanged. - If you had not specified
[count], the function would always contain a "frozen"countfrom the first render.
Important to remember
| Rule | Explanation |
|---|---|
useCallback(fn, deps) caches the function reference | The function is created anew only when the dependencies change |
| If you skip the dependencies | The function is recreated on every render (memoization does not work) |
If you set [] | The function is created once at mount and never updated |
| Specify every value used inside | Otherwise the closure will hold stale data |
Summary
| Question | Answer |
|---|---|
When to use useCallback() | When you need a function to keep its reference across renders (React.memo, useEffect, useMemo) |
| What happens without dependencies | The function is recreated every render - memoization does not work |
What happens with [] | The function is created once and never updated |
| What it returns | The same function, but with a memoized reference |
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.