useMemo vs useCallback vs React.memo
useMemo vs useCallback vs React.memo
In technical interviews, candidates are frequently asked to explain the differences between React’s three primary performance optimization tools: useMemo, useCallback, and React.memo.
While they all deal with caching (memoization) to prevent unnecessary work, they are used for completely different things.
1. React.memo (Memoizing a Component)
What it does: It is a Higher-Order Component (HOC). It wraps an entire React component and caches the rendered output.
By default, if a Parent component re-renders, React will force all of its Children to re-render, even if their props haven’t changed. If you wrap a Child in React.memo, you break this rule. React will only re-render the Child if its props have actually changed.
When to use it: Use it on components that are extremely heavy to render (like a massive data table or an complex interactive chart) and that frequently receive the exact same props from their parent.
// This component will ONLY re-render if 'data' changes.
// It will ignore parent renders otherwise.
const HeavyChart = React.memo(function HeavyChart({ data }) {
return <ComplexSVGChart data={data} />;
});
2. useMemo (Memoizing a Value)
What it does: It is a hook. It caches the result of an expensive calculation inside a component.
During a standard re-render, React will re-execute every single line of code in your component function. If you have a complex mathematical calculation or a massive array sort, it will run again on every render. useMemo caches the result of that calculation and only re-runs it if its dependencies change.
When to use it: Use it when you are deriving data that takes a noticeable amount of CPU time to compute (e.g., sorting/filtering an array of 5,000 items).
function DataGrid({ users, sortOrder }) {
// We ONLY run this massive sort if 'users' or 'sortOrder' changes.
// If the component re-renders for some other reason, we reuse the cached result.
const sortedUsers = useMemo(() => {
return [...users].sort((a, b) => expensiveCompare(a, b, sortOrder));
}, [users, sortOrder]);
return <div>...</div>;
}
Note: You should also use useMemo if you are passing an object or array as a prop to a React.memo child component, to ensure the object reference remains stable across renders.
3. useCallback (Memoizing a Function)
What it does: It is a hook. It is almost identical to useMemo, but it is specifically designed to cache a function definition, rather than the result of calling a function.
Every time a component re-renders, it creates brand new function references in memory. const handleClick = () => {} creates a completely different function object on Render 1 vs Render 2.
When to use it: You almost exclusively use useCallback when you need to pass a callback function down as a prop to a Child component that is wrapped in React.memo.
function Parent() {
const [count, setCount] = useState(0);
// If we didn't use useCallback here, this function would be recreated
// on every render. Because the function reference would change,
// the <ExpensiveChild /> would see a "new" prop and be forced to re-render,
// completely defeating the purpose of React.memo!
const handleSubmit = useCallback(() => {
api.submit();
}, []);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<ExpensiveChild onSubmit={handleSubmit} />
</div>
);
}
const ExpensiveChild = React.memo(({ onSubmit }) => {
/* ... */
});
Summary Table
| Tool | What does it cache? | What is it applied to? |
|---|---|---|
| React.memo | Rendered UI | A Component (e.g., function Button()) |
| useMemo | A calculated value | A variable (e.g., const sortedArr = ...) |
| useCallback | A function reference | A function (e.g., const handleClick = () => {}) |
Interview Questions
Q: Summarize exactly when to use React.memo, useMemo, and useCallback.
A:
React.memo: Wraps a component to prevent it from re-rendering if its props haven’t changed.useMemo: Caches the result of an expensive mathematical calculation or array iteration.useCallback: Caches a function definition so its memory reference doesn’t change when passed as a prop to a memoized child component.