Back to list
ReactHookuseMemoMemoizationPerformance

[React][Hook] Optimize Performance with useMemo and Memoization

Learn how memoization works with useMemo so heavy filters do not re-run when unrelated state like dark mode changes - and when not to wrap everything.

1. Environment

  • OS: Windows 11 / macOS
  • Frontend library: React.js (v18+)
  • Core concept: React Hooks (useMemo, memoization)
  • IDE: Visual Studio Code (VS Code)

2. Problem

This post digs into the real reason useMemo exists: memoization. We look at how it blocks pointless recalculation whenever state changes, and how that can lift frontend performance.

Related: Correct use of useEffect vs useMemo

3. Cause

When any state inside a React component changes, React re-runs the whole component function from top to bottom (a re-render). That simple, powerful rule can also cause surprising slowdowns.

Imagine one component that both runs a heavy calculation over 10,000 users and has a button that only toggles dark/light theme:

import { useState } from 'react';

function UserDashboard({ users }) {
  const [isDarkMode, setIsDarkMode] = useState(false);

  // Problem: this runs on every render
  const activeUsers = users.filter((user) => {
    console.log('Running an expensive calculation...');
    return user.isActive;
  });

  return (
    <div className={isDarkMode ? 'dark' : 'light'}>
      <button onClick={() => setIsDarkMode(!isDarkMode)}>Toggle theme</button>
      <p>Active users: {activeUsers.length}</p>
    </div>
  );
}

What happens when the user clicks Toggle theme?

Only the background should change - but because isDarkMode changed, React re-reads the entire component. Every theme click re-filters 10,000 users and loads the UI for no good reason.

4. Solution

1) What is memoization?

Memoization means: write down a hard result once, then reuse it instead of computing again when the inputs are the same.

If you calculated 12345 × 67890 = 838102050 yesterday, and someone asks for the same product today, you read the note - you do not multiply again.

2) Fix it with useMemo

The key is the dependency array [users]. It tells React:

“Recalculate activeUsers only when users changes. If users is unchanged and something else (like isDarkMode) caused the re-render, do not recalculate - reuse the cached value.|–

import { useState, useMemo } from 'react';

function UserDashboard({ users }) {
  const [isDarkMode, setIsDarkMode] = useState(false);

  // Recalculate only when users changes
  const activeUsers = useMemo(() => {
    console.log('Running an expensive calculation...');
    return users.filter((user) => user.isActive);
  }, [users]); // dependency array

  return (
    <div className={isDarkMode ? 'dark' : 'light'}>
      <button onClick={() => setIsDarkMode(!isDarkMode)}>Toggle theme</button>
      <p>Active users: {activeUsers.length}</p>
    </div>
  );
}

After this change, you can toggle the theme a hundred times and the heavy filter still runs only when users actually changes.

5. Summary

Wrapping every variable in useMemo because “it improves performance|–is a bad idea.

Caching a value and comparing the dependency array on every render also costs memory and CPU. A trivial line like const fullName = firstName + lastName can finish faster than the bookkeeping of memoization.

Use useMemo selectively for work that is actually expensive - list filtering/sorting over large arrays, or heavy math. If a stable, costly calculation is slowing renders, that is when this Hook earns its place.

Related posts