Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Understanding useMemo and useCallback in React

useMemo and useCallback

~11 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

These two hooks belong together: both "remember" (memoize) something across render passes to avoid unnecessary, repeated work. useMemo remembers a computed VALUE, useCallback remembers a FUNCTION.

The underlying problem: every state change re-runs the whole function

Important to understand: on EVERY state change, React re-runs the entire component function – not just the part that visibly changes. That means EVERY variable, EVERY calculation, and EVERY function inside the component gets recreated on every render. For simple calculations (const sum = a + b) that's completely fine – millions per second are no problem. But for GENUINELY expensive calculations (sorting large lists, complex filtering), it can become noticeable.

useMemo(): remembering a computed value

import { useMemo } from 'react';

const mostExpensiveValue = useMemo(() => {
  console.log('Running the expensive calculation...');
  return products.reduce((max, p) => (p.price > max ? p.price : max), 0);
}, [products]);

useMemo(calculation, [dependencies]) only re-runs calculation when one of the values in the dependency array has changed since the last render – if, say, only an UNRELATED state value changes (like the search field, if it's not in the dependencies), the cached ("memoized") old value is simply reused, without re-running the calculation.

Extending App.jsx: showing the most expensive price

Extend src/App.jsx with a useMemo-computed statistic:

src/App.jsx
import { useEffect, useMemo, useRef, useState } from 'react';
import ProductCard from './components/ProductCard';

const SAMPLE_PRODUCTS = [
  { sku: 'boots-01', name: 'Hiking Boots', price: 89.99, imageUrl: 'https://picsum.photos/seed/1/300' },
  { sku: 'backpack-01', name: 'Trekking Backpack', price: 59.5, imageUrl: 'https://picsum.photos/seed/2/300' },
  { sku: 'jacket-01', name: 'Rain Jacket', price: 74.0, imageUrl: 'https://picsum.photos/seed/3/300' },
];

function App() {
  const [query, setQuery] = useState('');
  const searchInputRef = useRef(null);

  useEffect(() => {
    document.title = `Product Catalog (${SAMPLE_PRODUCTS.length} products)`;
    searchInputRef.current.focus();
  }, []);

  const filtered = SAMPLE_PRODUCTS.filter((product) =>
    product.name.toLowerCase().includes(query.toLowerCase())
  );

  const mostExpensiveProduct = useMemo(() => {
    console.log('Recalculating most expensive product...');
    return filtered.reduce(
      (max, p) => (p.price > (max?.price ?? 0) ? p : max),
      null
    );
  }, [filtered]);

  return (
    <div className="app">
      <h1>Product Catalog</h1>
      <input
        ref={searchInputRef}
        type="text"
        placeholder="Search products..."
        value={query}
        onChange={(event) => setQuery(event.target.value)}
      />
      {mostExpensiveProduct && (
        <p>Most expensive product: {mostExpensiveProduct.name} (${mostExpensiveProduct.price.toFixed(2)})</p>
      )}
      <div className="product-grid">
        {filtered.map((product) => (
          <ProductCard
            key={product.sku}
            name={product.name}
            price={product.price}
            imageUrl={product.imageUrl}
            onSelect={() => alert(product.name + ' tapped')}
          />
        ))}
      </div>
    </div>
  );
}

export default App;

max?.price ?? 0 uses "optional chaining" (?., returns undefined instead of an error when max is null) and the "nullish coalescing operator" (??, only supplies the right-hand value for null/undefined, unlike ||, which would also kick in for 0).

useCallback(): remembering a function

useCallback works exactly like useMemo, but instead of a value it remembers a FUNCTION itself – more precisely: the same function REFERENCE across multiple render passes, as long as the dependencies don't change.

import { useCallback } from 'react';

const handleProductSelect = useCallback((product) => {
  console.log('Selected:', product.name);
}, []); // no dependencies = always the same function reference

Without useCallback, EVERY render creates a BRAND-NEW function – even if its code looks identical, from JavaScript's perspective it's a new object in memory (fn1 === fn2 would be false). Usually that doesn't matter – but when that function is passed to a child component optimized with React.memo (more on this in the Performance chapter of the React für Profis tutorial), a "new" function reference on every render defeats exactly the optimization React.memo was supposed to provide.

Achtung: Important rule of thumb for beginners: do NOT sprinkle useMemo/useCallback everywhere preemptively. They carry a small overhead themselves (React has to remember and compare the dependencies), which costs more than it saves for simple calculations/functions. Use them deliberately: for GENUINELY expensive calculations, or when you have a concrete, measurable performance problem – not "just in case".