useMemo in React: When It Boosts Performance (and When It Doesn’t)

A beginner-friendly guide to mastering memoisation, avoiding common pitfalls, and understanding how React 19 changes the game

useMemo Thumbnail

If you’ve worked with React for a while, you’ve probably heard about useMemo. It’s often mentioned in performance discussions—but many developers either overuse it or avoid it entirely due to confusion.

Let’s break it down in a simple, practical way so you can use it confidently (and correctly).

What is useMemo in React?

At its core useMemo is a performance optimisation hook.

It stores (or caches) the result of a computation and only recalculates it when its dependencies change. This helps avoid repeating expensive work on every render.

Basic syntax:

const memoizedValue = useMemo(() => {
return computeSomething();
}, [dependencies]);

How it works:

  • On the first render → function runs, and the result is stored
  • On re-render → React checks dependencies
  • If dependencies haven’t changed → cached value is reused
  • If they changed → function runs again

React uses Object.is internally to compare dependency values.

When should you use useMemo?

useMemo is most useful when your app performs heavy computations or handles large data sets.

Common use cases:

1. Filtering or sorting large lists

const completedTodos = useMemo(() => {
return todos.filter(todo => todo.completed);
}, [todos]);

2. Complex calculations

For example, pricing logic, analytics calculations, or aggregations.

3. Derived state

When you compute values from props or context instead of storing them directly.

Understanding Dependencies (The Real Key)

The effectiveness of useMemo depends heavily on correct dependency management.

Important concept: Referential Equality

In JavaScript:

  • Primitives (number, string) compare by value ✅
  • Objects & arrays compare by reference ❌

This means:

useMemo(() => calculate(), [{}]); // ❌ Bad

Why? Because {} creates a new object on every render → memoization breaks.

Correct approach:

  • Use stable references
  • Memoise objects/functions if needed
const obj = useMemo(() => ({ a: 1 }), []);

useMemo vs React.memo: Don’t Confuse Them

This is one of the most common mistakes.

Comparison image

❌ Incorrect usage:

const element = useMemo(() => <Child />, []);

This returns a React element (object), not a component — leading to potential issues.

✅ Correct usage:

const MemoizedChild = React.memo(Child);

👉 Simple rule:
useMemo → data optimisation
React.memo → component optimisation

Optimising React Context with useMemo

If you’ve used Context API, you might have noticed unnecessary re-renders.

Problem:

const value = { user, setUser }; // new object every render

Even if user hasn’t changed, all consumers re-render.

Solution:

const value = useMemo(() => ({ user, setUser }), [user]);

Now:

  • The object reference stays stable
  • Consumers only re-render when user changes

Advanced Pattern: useMemo + useCallback

For better stability:

const signOut = useCallback(() => setUser(null), []);
const contextValue = useMemo(() => ({
user,
signOut
}), [user, signOut]);

Why this works:

  • useCallback stabilizes functions
  • useMemo stabilizes objects

Does useMemo Really Improve Performance?

It depends.

Benchmark insights:

  • Heavy computations → up to ~60–70% improvement
  • Simple operations → negligible difference

Important observation:

  • useMemo alone → small gains
  • Combined with component memoization → significant improvement

However…

⚠️ Overusing it can:

  • Increase memory usage
  • Add complexity
  • Cause bugs (if dependencies are wrong)

React 19: Do You Still Need useMemo?

With the upcoming React Compiler, things are changing.

What it does:

  • Automatically optimises re-renders
  • Memoises values and functions
  • Stabilizes references

What this means:

  • You’ll write less manual useMemo
  • Optimisation becomes mostly automatic

But it’s still useful for:

  • Third-party libraries (charts, drag-and-drop)
  • Heavy external data processing
  • Older codebases without compiler support

So, it’s not going away — just becoming less common.

Common Mistakes to Avoid

❌ Using hooks inside useMemo

useMemo(() => {
useState(0); // invalid
});

❌ Memoizing JSX

useMemo(() => <Component />, []); // wrong

❌ Conditional usage

if (condition) useMemo(() => ...); // breaks rules

❌ Using for side effects

useMemo(() => {
localStorage.setItem('key', value);
}, [value]); // wrong

👉 Use useEffect for side effects.

Best Practices (What Actually Works)

✅ Use it when:

  • Computation is expensive
  • Data size is large
  • You need stable references

✅ Combine with:

  • useCallback → for functions
  • React.memo → for components

✅ Always:

  • Profile first (React DevTools)
  • Keep dependencies accurate

Key Takeaways

  • useMemo caches computed values to avoid unnecessary recalculations
  • It’s most useful for expensive operations, not small ones
  • Dependency management is critical — wrong dependencies = bugs
  • Don’t confuse it with React.memo (data vs components)
  • It plays a big role in optimising Context API performance
  • Overusing it can hurt more than help
  • With React 19, manual optimisation will reduce — but not disappear
If you like this article, please do support me by clapping on this article, For you it might take some seconds for me it provides me huge motivation to keep writing more.

At Dev Simplified, We Value Your Feedback 📊

👉 To read more such articles on React, visit here

👉 Follow us not to miss any updates.

👉 Have any suggestions? Let us know in the comments!

👉 Subscribe for free and join our growing community!