For years, useMemo
and useCallback
have been the go-to tools for React developers looking to optimize performance by avoiding unnecessary computations and function re-creations. However, with the latest advancements in React, their necessity has significantly diminished. In this post, I’ll break down why they are now mostly redundant and when you might still need them.
The Original Use Case: Preventing Unnecessary Renders
React’s reconciliation algorithm is quite efficient, but unnecessary re-renders can still impact performance, especially in complex applications. The idea behind useMemo
and useCallback
was simple:
useMemo
: Caches the result of a computation, preventing recalculations unless dependencies change.useCallback
: Caches a function reference to prevent child components from unnecessarily re-rendering when passed as props.
This made sense in the era when React re-renders were more aggressive and when components frequently relied on referential equality to determine updates.
Why They’re No Longer Necessary in Most Cases
1. Modern React Optimizations Make Them Redundant
With React 18’s Concurrent Mode, automatic batching, and improvements in React Compiler optimizations (like RSC and React Forget), React has become significantly smarter about avoiding unnecessary re-renders. Components now update more efficiently, reducing the need for manual memoization.
- Automatic Memoization in React Compiler: The React team is working on a compiler that will optimize component re-renders automatically, removing the need for
useMemo
anduseCallback
in most cases. - Rendering Scheduling and Priority Management: React now schedules renders more intelligently, making unnecessary recomputations a smaller issue than before.
2. Garbage Collection & Memory Pressure Issues
One of the problems with useMemo
and useCallback
is that they hold onto references longer than necessary, leading to unnecessary memory retention. In some cases, their overuse can actually harm performance rather than help.
3. Inline Functions Are Cheap in Modern JavaScript Engines
Many developers use useCallback
out of habit, believing it prevents unnecessary re-renders. But in most cases, inline functions are already optimized by modern JavaScript engines, making useCallback
redundant.
- Modern V8 optimizations ensure that functions declared inline inside a component are efficiently garbage-collected and reallocated.
- Referential equality checks are not always meaningful in React reconciliation, as components don’t always rely on the same function reference.
4. React.memo Is Usually a Better Approach
Instead of manually memoizing functions with useCallback
, simply wrapping a component with React.memo
is often the more effective and concise solution.
const ExpensiveComponent = React.memo(({ value }) => {
console.log("Rendering...");
return <div>{value}</div>;
});
export default function Parent({ count }) {
return <ExpensiveComponent value={count} />;
}
React.memo
ensures that ExpensiveComponent
only re-renders when value
actually changes—without needing useCallback
.
When Should You Still Use useMemo
or useCallback
?
While largely unnecessary, useMemo
and useCallback
still have valid use cases:
-
Computationally Expensive Operations
- If a component performs expensive calculations, memoizing the result with
useMemo
prevents unnecessary recomputation.
const result = useMemo(() => computeExpensiveValue(data), [data]);
- If a component performs expensive calculations, memoizing the result with
-
Memoizing Dependencies in Custom Hooks
- If a custom hook depends on function identity,
useCallback
ensures the function reference doesn’t change between renders.
const fetchData = useCallback(() => fetch(apiUrl), [apiUrl]); useEffect(() => { fetchData(); }, [fetchData]);
- If a custom hook depends on function identity,
-
Preventing Unnecessary Renders in Context Providers
- When passing functions down through React Context,
useCallback
can help prevent unnecessary re-renders of consumers.
- When passing functions down through React Context,
-
Avoiding Unnecessary Object Creation in Dependency Arrays
- When passing objects as dependencies in hooks like
useEffect
,useMemo
ensures stable object references.
const memoizedOptions = useMemo(() => ({ limit: 10 }), []);
- When passing objects as dependencies in hooks like
-
When You Notice a UX Problem
- The best rule of thumb is to start without
useMemo
anduseCallback
. Only add them when you experience noticeable performance issues to the human eye, such as lagging, stuttering animations, or delays in rendering complex components. - Instead of preemptively optimizing, profile your application and add these hooks when a real bottleneck is identified.
- The best rule of thumb is to start without
Conclusion: Less Is More
The takeaway? In modern React apps, you should default to not using useMemo
and useCallback
unless you truly need them. Overusing them leads to unnecessary complexity, potential memory overhead, and ironically, worse performance.
Instead, trust React’s rendering optimizations and use these hooks only when profiling reveals a bottleneck. The era of manually memoizing everything is fading—React itself is becoming smart enough to handle performance for us.