React provides a set of powerful hooks to manage state and side effects in functional components. One such hook is useCallback
, which helps optimize performance in certain situations.
What is useCallback
?
useCallback
is a React Hook that returns a memoized version of a callback function. This means that React will remember the function you pass and only recreate it when its dependencies change.
It’s commonly used to prevent unnecessary re-creation of functions, especially when passing them to child components or using them in dependencies.
Syntax
const memoizedCallback = useCallback(() => {
// your function logic
}, [dependencies]);
() => {}
: This is your callback function.[dependencies]
: This is the dependency array. If any value here changes, the function will be re-created.
Why Use useCallback
?
In React, every time a component renders, functions are re-created. This is usually fine, but in some cases, especially when:
- A function is passed to a child component as a prop,
- Or used inside an effect like
useEffect
oruseMemo
,
…it can cause unnecessary re-renders or logic re-executions.
useCallback
helps avoid this by caching the function, so its reference stays the same unless it actually needs to change.
Problem Without useCallback
Here’s an example where a child component receives a function as a prop:
function Parent() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
return <Child onClick={increment} />;
}
Every time Parent
re-renders, the increment
function is re-created. This causes the Child
component to think the prop changed, even if it didn’t.
Solution Using useCallback
function Parent() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return <Child onClick={increment} />;
}
Now, increment
has a stable reference. It only changes if the dependencies (in this case, none) change. So the Child
component won’t re-render unnecessarily.
Real-World Example with React.memo
const Button = React.memo(({ onClick, label }) => {
console.log("Rendering:", label);
return <button onClick={onClick}>{label}</button>;
});
function App() {
const [count, setCount] = useState(0);
const [theme, setTheme] = useState("light");
const increment = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return (
<>
<Button onClick={increment} label="Increment" />
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Toggle Theme
</button>
<div>Count: {count}</div>
</>
);
}
Here, the Increment
button won’t re-render when we toggle the theme, because increment
is memoized with useCallback
.
When to Use useCallback
Use useCallback
only when needed — such as when:
- You’re passing a callback to a memoized child component (
React.memo
). - The callback is used in a dependency array (like in
useEffect
,useMemo
).
Tip: Performance Consideration
Using useCallback
everywhere does not improve performance — in fact, it can hurt performance if overused because it also takes memory and processing to manage dependencies. Only use it when a function being re-created causes an issue.
Conclusion
useCallback
is a helpful optimization tool in React that memoizes your functions and avoids unnecessary re-renders or executions. Use it wisely when you’re dealing with performance-sensitive components, especially when passing callbacks to children or inside dependency arrays.