Skip to main content
useMemo returns a memoized value. It only recomputes the value when one of the dependencies has changed, helping to avoid expensive calculations on every render.

Signature

function useMemo<T>(
  factory: () => T,
  inputs: ReadonlyArray<unknown> | undefined
): T

Parameters

factory
() => T
required
A function that computes and returns the value to be memoized. This function should be pure and take no arguments.
inputs
ReadonlyArray<unknown> | undefined
required
An array of dependencies. The memoized value will only be recomputed when one of these values changes (compared using ===). If undefined is passed, a new value will be computed whenever a new function instance is passed as the first argument.

Returns

Returns the memoized value of type T. On the initial render, it returns the result of calling factory(). On subsequent renders, it returns the cached value if dependencies haven’t changed, or recomputes by calling factory() if they have.

Basic Usage

import { useMemo, useState } from 'preact/hooks';

function ExpensiveComponent({ items }) {
  const [filter, setFilter] = useState('');

  // Only recompute when items or filter changes
  const filteredItems = useMemo(() => {
    console.log('Filtering items...');
    return items.filter(item =>
      item.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [items, filter]);

  return (
    <div>
      <input value={filter} onChange={e => setFilter(e.target.value)} />
      <ul>
        {filteredItems.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

Expensive Calculations

function DataAnalysis({ data }) {
  const statistics = useMemo(() => {
    console.log('Computing statistics...');
    return {
      mean: data.reduce((sum, val) => sum + val, 0) / data.length,
      max: Math.max(...data),
      min: Math.min(...data),
      sum: data.reduce((sum, val) => sum + val, 0)
    };
  }, [data]);

  return (
    <div>
      <p>Mean: {statistics.mean}</p>
      <p>Max: {statistics.max}</p>
      <p>Min: {statistics.min}</p>
      <p>Sum: {statistics.sum}</p>
    </div>
  );
}

Memoizing Objects

function UserProfile({ userId, userName }) {
  // Prevent creating a new object on every render
  const user = useMemo(() => ({
    id: userId,
    name: userName,
    lastSeen: Date.now()
  }), [userId, userName]);

  return <ExpensiveChildComponent user={user} />;
}

Sorting and Filtering

function ProductList({ products, sortBy, searchTerm }) {
  const displayedProducts = useMemo(() => {
    let result = products;

    // Filter
    if (searchTerm) {
      result = result.filter(p =>
        p.name.toLowerCase().includes(searchTerm.toLowerCase())
      );
    }

    // Sort
    result = [...result].sort((a, b) => {
      if (sortBy === 'price') return a.price - b.price;
      if (sortBy === 'name') return a.name.localeCompare(b.name);
      return 0;
    });

    return result;
  }, [products, sortBy, searchTerm]);

  return (
    <ul>
      {displayedProducts.map(product => (
        <li key={product.id}>{product.name} - ${product.price}</li>
      ))}
    </ul>
  );
}

Computing Derived State

function ShoppingCart({ items }) {
  const summary = useMemo(() => {
    const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
    const tax = subtotal * 0.1;
    const shipping = subtotal > 100 ? 0 : 10;
    const total = subtotal + tax + shipping;

    return { subtotal, tax, shipping, total };
  }, [items]);

  return (
    <div>
      <p>Subtotal: ${summary.subtotal.toFixed(2)}</p>
      <p>Tax: ${summary.tax.toFixed(2)}</p>
      <p>Shipping: ${summary.shipping.toFixed(2)}</p>
      <p>Total: ${summary.total.toFixed(2)}</p>
    </div>
  );
}

Referential Equality

import { memo } from 'preact/compat';

// Child component only re-renders if style object reference changes
const ExpensiveChild = memo(({ style }) => {
  console.log('Child rendered');
  return <div style={style}>Expensive component</div>;
});

function Parent({ color }) {
  // Memoize style object to prevent unnecessary child re-renders
  const style = useMemo(() => ({
    backgroundColor: color,
    padding: '20px',
    borderRadius: '8px'
  }), [color]);

  return <ExpensiveChild style={style} />;
}

Creating Regex Patterns

function SearchableList({ items, pattern }) {
  const regex = useMemo(() => {
    try {
      return new RegExp(pattern, 'i');
    } catch {
      return null;
    }
  }, [pattern]);

  const filteredItems = useMemo(() => {
    if (!regex) return items;
    return items.filter(item => regex.test(item.name));
  }, [items, regex]);

  return (
    <ul>
      {filteredItems.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

Graph Computations

function SocialGraph({ users, connections }) {
  const adjacencyList = useMemo(() => {
    const graph = new Map();

    users.forEach(user => {
      graph.set(user.id, new Set());
    });

    connections.forEach(([from, to]) => {
      graph.get(from)?.add(to);
      graph.get(to)?.add(from);
    });

    return graph;
  }, [users, connections]);

  const getMutualFriends = (userId1, userId2) => {
    const friends1 = adjacencyList.get(userId1) || new Set();
    const friends2 = adjacencyList.get(userId2) || new Set();

    return [...friends1].filter(id => friends2.has(id));
  };

  return <div>{/* Use adjacencyList */}</div>;
}

With undefined Dependencies

function Component({ computeFn }) {
  // Recompute whenever computeFn reference changes
  const result = useMemo(() => {
    return computeFn();
  }, undefined);

  return <div>{result}</div>;
}
useMemo is an optimization tool. Your code should work correctly without it, then add it to improve performance where needed.
Don’t use useMemo for everything. It has overhead, so only use it for computationally expensive operations or when maintaining referential equality is important for child component optimization.
Unlike React, Preact requires the inputs parameter. Pass undefined explicitly if you want to recompute whenever the factory function changes.

Build docs developers (and LLMs) love