useCallback
useCallback
useCallback is a React Hook that lets you cache a function definition between renders.
It is very similar to useMemo, but it is specifically designed for caching functions rather than the results of function calls.
Syntax
const cachedFn = useCallback(fn, dependencies)
fn: The function value that you want to cache.dependencies: An array of reactive values (props, state) referenced inside the function.
The Problem it Solves
In JavaScript, a function () {} or () => {} always creates a different function instance every time it is evaluated, much like how {} always creates a new object.
When a parent component re-renders, any inline functions defined inside it are re-created. If you pass those functions as props to a child component, the child component will see a new prop value and will re-render—even if you wrapped the child in React.memo to prevent unnecessary renders!
Example
import { useState, useCallback, memo } from 'react';
// A heavy component wrapped in React.memo so it only re-renders when props change
const ExpensiveForm = memo(function ExpensiveForm({ onSubmit }) {
// ... heavy rendering ...
});
function App() {
const [text, setText] = useState('');
const [productId, setProductId] = useState(1);
// ❌ BAD: This function is re-created every time 'text' changes!
// ExpensiveForm will re-render on every keystroke.
// const handleSubmit = (data) => post('/api', { id: productId, data });
// ✅ GOOD: The function reference stays exactly the same unless 'productId' changes.
// ExpensiveForm will NOT re-render when the user types in the text input.
const handleSubmit = useCallback((data) => {
post('/api', { id: productId, data });
}, [productId]);
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
<ExpensiveForm onSubmit={handleSubmit} />
</div>
);
}
useCallback vs useMemo
Under the hood, useCallback(fn, deps) is exactly equivalent to useMemo(() => fn, deps).
- Use
useMemoto cache the result of calling a function. - Use
useCallbackto cache the function itself.
When NOT to use useCallback
Just like useMemo, useCallback should not be used everywhere prematurely.
If you are passing a function down to standard HTML elements (like <button onClick={handleClick}>), useCallback provides zero performance benefit and only adds overhead. You should only use it when passing functions as props to custom components that are specifically optimized with React.memo, or when the function is used as a dependency in another Hook (like useEffect).
Interview Questions
Q: What does useCallback do?
A: It memoizes (caches) a function definition between re-renders. Normally, a new function is created on every render. useCallback ensures that the function reference remains exactly the same unless its dependencies change.
Q: Should I wrap every function in useCallback to improve performance?
A: No, this is a common anti-pattern. Creating a function is incredibly cheap in JavaScript. You should only use useCallback when passing a function down as a prop to a deeply nested, memoized child component (like a React.memo component) to prevent the child from unnecessarily re-rendering, or when the function is used in a useEffect dependency array.