How (and when) to use useMemo() and useCallback()
Many React developers struggle to understand the difference between useMemo() and useCallback() and when to use each of these hooks. This post explains both with practical examples.
By Vasili Zalimidis —
Many React developers struggle to understand the difference between useMemo() and useCallback() and when to use each of these hooks. In this blog post I'll try to explain this and make it a bit clearer for you.
Please note: It's best if you read this blog post after you have some knowledge of React. If you're an absolute React beginner, I'd suggest you bookmark this post and come back in a couple of weeks.
Re-rendering in React
React "re-renders" to keep our UI in sync with our application state as it gets updated. Each re-render is a "snapshot" of the UI in a specific moment, based on the application's state.
While React is greatly optimized to manage these updates, it might take a while to create these snapshots under certain circumstances, making the UI updates feel "slow" or "laggy". This is where useMemo() and useCallback() come into play.
useMemo() hook
useMemo() allows us to "memorize" a value between renders. This can be helpful when we have heavy computations to make or when we need to preserve references between renders.
Case 1: Heavy computations
Let's suppose we create a tool that returns the fibonacci sequence up to a given number. We need to perform the calculations sometimes (when user changes the input) but not on every render.
const fibonacciSuite = React.useMemo(() => {
return getFibonacciSuiteNumbers(selectedNum);
}, [selectedNum]);
useMemo takes two arguments:
- A block of work to be executed, wrapped as a function.
- A list of dependencies.
React checks if any dependency has changed. If so, it re-runs the function. Otherwise, it reuses the previously calculated value. This is known as memoization.
Case 2: Preserving references
In JavaScript, every time React re-renders, a new array is created. Two arrays can be equivalent in value but reference different objects:
const a = [1, 2, 3];
const b = [1, 2, 3];
console.log(a === b); // false — same values, different references
We can solve this with useMemo:
const cards = React.useMemo(() => {
return [
{ cardId: 0, cardContent: 'I am a card' },
{ cardId: 1, cardContent: cardText },
];
}, [cardText]);
useCallback() hook
useCallback() is the same exact thing as useMemo(), but for functions instead of arrays/objects. Functions are also compared by reference — a function defined within a component is replaced on every single render.
The following two examples have the same result:
React.useCallback(function helloWorld() {}, []);
// is the same as
React.useMemo(() => function helloWorld() {}, []);
useCallback is syntactic sugar — it exists to make our lives easier when memoizing functions.
When should I use them?
The best moment to use these hooks is when you really have issues with performance. When you notice your app is getting sluggish, use the React profiler to narrow the issue.
Here are some scenarios where I use these hooks without hesitation:
Context providers
const AuthContext = React.createContext({});
function AuthProvider({ user, status, children }) {
const memoizedAuthState = React.useMemo(() => {
return { user, status };
}, [user, status]);
return (
<AuthContext.Provider value={memoizedAuthState}>
{children}
</AuthContext.Provider>
);
}
Since this context might be consumed by many pure components, useMemo prevents unnecessary re-renders whenever AuthProvider's parent re-renders.