React useMemo

Optimizing expensive calculations in React

💾 What is useMemo?

useMemo is a React Hook that memoizes computed values. It caches the result of expensive calculations and only recalculates when dependencies change, improving performance.


import { useMemo } from 'react';

function MyComponent({ items }) {
  const total = useMemo(() => {
    return items.reduce((sum, item) => sum + item.price, 0);
  }, [items]);
  
  return <p>Total: ${total}</p>;
}
                                    

useMemo Concepts

🧮

Memoization

Caches computed values between renders

const value = useMemo(() => compute(), deps);
⚙️

Expensive Calculations

Optimizes heavy computations

const result = useMemo(() => heavyCalc(data), [data]);
📊

Dependencies

Recalculates when deps change

useMemo(fn, [dep1, dep2]);
🎯

Return Value

Returns the memoized result

const memoizedValue = useMemo(...);

🔹 Basic useMemo Example

useMemo prevents recalculating expensive operations on every render. This example shows filtering a large list only when the filter or list changes.

import { useState, useMemo } from 'react';

function ProductList({ products }) {
  const [filter, setFilter] = useState('');

  // Only recalculates when products or filter changes
  const filteredProducts = useMemo(() => {
    console.log('Filtering products...');
    return products.filter(product =>
      product.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [products, filter]);

  return (
    <div>
      <input
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="Search products..."
      />
      <p>Found {filteredProducts.length} products</p>
      <ul>
        {filteredProducts.map(product => (
          <li key={product.id}>{product.name} - ${product.price}</li>
        ))}
      </ul>
    </div>
  );
}

Output:

Found 3 products

  • Laptop - $999
  • Mouse - $29
  • Keyboard - $79

🔹 Expensive Calculation Example

useMemo shines with computationally expensive operations. This factorial calculator only recalculates when the number changes, not on every render.

import { useState, useMemo } from 'react';

function FactorialCalculator() {
  const [number, setNumber] = useState(5);
  const [theme, setTheme] = useState('light');

  // Expensive calculation - only runs when number changes
  const factorial = useMemo(() => {
    console.log('Calculating factorial...');
    let result = 1;
    for (let i = 1; i <= number; i++) {
      result *= i;
    }
    return result;
  }, [number]);

  return (
    <div className={theme}>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(Number(e.target.value))}
        min="0"
        max="20"
      />
      <p>Factorial of {number} is: {factorial}</p>
      
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
      <p>Factorial doesn't recalculate when theme changes!</p>
    </div>
  );
}

Output:

Factorial of 5 is: 120

Factorial doesn't recalculate when theme changes!

🔹 useMemo with Object/Array

useMemo is useful for creating stable object or array references. This prevents unnecessary re-renders in child components that depend on these values.

import { useState, useMemo } from 'react';

function UserProfile({ userId }) {
  const [theme, setTheme] = useState('light');

  // Object reference stays the same if userId doesn't change
  const userConfig = useMemo(() => {
    return {
      id: userId,
      settings: {
        theme: 'auto',
        notifications: true
      }
    };
  }, [userId]);

  // Array reference stays the same
  const permissions = useMemo(() => {
    return ['read', 'write', 'delete'];
  }, []); // Empty deps - never changes

  return (
    <div>
      <p>User ID: {userConfig.id}</p>
      <p>Permissions: {permissions.join(', ')}</p>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </div>
  );
}

🔹 When to Use useMemo

useMemo should be used strategically for performance optimization:

✅ Use useMemo when:

  • Expensive calculations: Complex computations, sorting, filtering large arrays
  • Referential equality matters: Value passed to memoized child components
  • Derived state: Computing values from props or state
  • Performance issues: Measurable slowdown without memoization

❌ Don't use useMemo when:

  • Simple calculations: Basic math, string operations
  • Premature optimization: No performance problem exists
  • Every value: Adds unnecessary complexity
  • Primitive values: Numbers, strings, booleans (unless expensive to compute)

🔹 useMemo vs useCallback

Both hooks memoize values, but they serve different purposes:

import { useMemo, useCallback } from 'react';

function Comparison() {
  // useMemo - memoizes the RESULT of a function
  const expensiveValue = useMemo(() => {
    return computeExpensiveValue();
  }, [dependency]);

  // useCallback - memoizes the FUNCTION itself
  const memoizedCallback = useCallback(() => {
    doSomething();
  }, [dependency]);

  // Equivalent to:
  const memoizedCallback2 = useMemo(() => {
    return () => doSomething();
  }, [dependency]);

  return <div>{expensiveValue}</div>;
}
  • useMemo: Returns a memoized VALUE
  • useCallback: Returns a memoized FUNCTION
  • useCallback(fn, deps): Same as useMemo(() => fn, deps)

🧠 Test Your Knowledge

What does useMemo return?