useMemo vs useCallback: 5 Mistakes That Slow Down Your React App
Understanding the essence of memoization is the foundation for avoiding common pitfalls and building a cleaner, more maintainable codebase. This article takes a close look at how the two hooks work, how they differ, and, more importantly, the situations where you absolutely should not use them.

Trung Vũ Hoàng
Author
1. What Is Memoization in React?
One thing to understand first: every operation in React has a cost. Each time a component re-renders, all variables and functions inside it are re-created from scratch.
Memoization is a technique that caches computed results or function definitions. When the inputs (dependencies) haven't changed, React returns the cached data instead of recalculating—saving CPU resources.
However, caching requires memory and time to compare dependencies on each render. Premature, thoughtless optimization often does more harm than good.
2. useMemo: Memoizing Computation Results
useMemo is a hook that caches a function's return value—especially useful when that function performs expensive computations.
How It Works
On the initial render, the function passed to useMemo runs as usual. From the second render onward, React checks the dependency array:
If a dependency changes: The function runs again to produce a new result.
If dependencies are unchanged: React returns the previously cached result—no work is redone.
Real-World Example
You have a list of 5,000 products and need to filter by the user's search keyword. This is a heavy computation—but it only needs to run when the list or the query actually changes:
const filteredProducts = useMemo(
() => filterLogic(products, query),
[products, query]
);3. useCallback: Memoize Function Definitions
While useMemo caches a value, useCallback caches the function you pass in—it doesn't execute the function, it just remembers it.
Why Memoize a Function?
In JavaScript, functions are objects. Whenever a component re-renders, functions declared inside it receive a new reference in memory—even if their body doesn't change.
This becomes a problem when you pass that function to a child component wrapped in React.memo. Because the new function is not the same reference as the old one, the child component re-renders needlessly.
When Does useCallback Actually Help?
When passing a function as a prop to a child component wrapped with
React.memo.When the function is a dependency of another hook such as
useEffect.
4. Key Differences
Purpose
useMemo: Cache a value—number, string, array, object...
useCallback: Cache a function (function reference).
Return value
useMemo: The result after the callback has executed.
useCallback: The callback function itself, not executed.
Syntax relationship
In fact, useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).
5. Five Common Memoization Mistakes
The habit of "wrapping everything in useMemo and useCallback" is a common anti-pattern. Here are practical reasons to stop:
Mistake 1: Dependency comparison costs outweigh the benefits
Comparing the dependency array on every render can cost more than recreating a simple function. Memoization only pays off when the work is truly expensive.
Mistake 2: Making code harder to read and maintain
Overusing these hooks turns a component into a maze of logic and dependencies. Teammates won't know where to start.
Mistake 3: Causing stale closures—the hardest bugs to track down
If you forget to include a variable in the dependency array, the function will read stale data—leading to silent logic bugs with no errors in the console.
Mistake 4: Applying it to primitive values
Types like number, string, and boolean are compared by value. Wrapping them in useMemo is completely redundant.
Mistake 5: Optimizing before measuring
Don't add memoization because you think something might be slow. Use React DevTools Profiler to identify real bottlenecks before intervening.
6. A Sound Optimization Strategy
A practical workflow I use in production projects:
Step 1: Write code naturally, without thinking about optimization.
Step 2: Use React DevTools Profiler to find components that re-render the most.
Step 3: Check whether those re-renders actually cause user-perceived lag. If not, ignore them.
Step 4: If optimization is needed, try refactoring the component (composition pattern) first.
Step 5: Only when necessary, use
useMemofor heavy computations anduseCallbackfor functions passed to children.
7. Summary
useMemo and useCallback are powerful when used in the right places, but unnecessary baggage when overused. Remember:
Use
useMemoto protect the results of expensive calculations.Use
useCallbackto keep function references stable when passing to children.Measure first, optimize later.
Have you ever added useMemo and found the app got slower? That's usually a sign of mistake #1 or #3 above!
Bài viết liên quan

Closures in JavaScript: Understand Them Thoroughly to Code Like a Pro
Closures—also known as "closed functions"—are one of the most powerful yet confusing concepts in JavaScript. Mastering closures not only helps you ace Senior-level interviews, but also changes how you architect your entire codebase: cleaner, safer, and less buggy. Many people confuse Scope and Closure, but they’re closely related and complementary. Let’s start from the foundations.

useEffect vs useMemo: 5 Key Differences You Need to Understand
Confusing useEffect and useMemo is one of the most common sources of performance issues in React apps. Think of useMemo as a calculator that remembers results, and useEffect as a switchboard operator waiting for external signals before acting.

useMemo vs useCallback: 3 React Optimization Tactics Senior Developers Use
Performance optimization in React is often seen as a minefield—where many developers get stuck making the wrong calls. Sprinkling useMemo and useCallback everywhere in hopes of speeding things up can backfire, forcing the CPU to do extra dependency comparisons for no reason. This article shows you how to use these two hooks like a pro.