---
source_hash: "e0be357f"
title: "Chapter 1: useCallback and useMemo"
weight: 450
---

# Chapter 1: useCallback and useMemo

## The Problem: Unnecessary Re-renders

Every time a component re-renders, all its code is re-executed:

```javascript
function ProductList({ products }) {
    // Recalculated on EVERY render, even if products hasn't changed
    const sortedProducts = products.sort((a, b) => a.price - b.price);

    // New function created on EVERY render
    const handleClick = (id) => {
        console.log('Clicked:', id);
    };

    return (
        <ul>
            {sortedProducts.map(p => (
                <ProductItem key={p.id} product={p} onClick={handleClick} />
            ))}
        </ul>
    );
}
```

If `products` contains 1000 elements, the sort is done on every render, even if nothing has changed.

## useMemo: Memoize a Value

`useMemo` memoizes the result of a calculation and only recalculates it when dependencies change.

### Syntax

```javascript
const memoizedValue = useMemo(() => computeValue(a, b), [a, b]);
```

### Example: Expensive Calculation

```javascript
import { useMemo } from 'react';

function ProductList({ products, sortBy }) {
    // Recalculated ONLY if products or sortBy changes
    const sortedProducts = useMemo(() => {
        console.log('Sorting...');
        return [...products].sort((a, b) => {
            if (sortBy === 'price') return a.price - b.price;
            if (sortBy === 'name') return a.name.localeCompare(b.name);
            return 0;
        });
    }, [products, sortBy]);

    return (
        <ul>
            {sortedProducts.map(p => (
                <li key={p.id}>{p.name} - {p.price}€</li>
            ))}
        </ul>
    );
}
```

### Example: Filtering

```javascript
function UserList({ users, searchTerm }) {
    const filteredUsers = useMemo(() => {
        return users.filter(user =>
            user.name.toLowerCase().includes(searchTerm.toLowerCase())
        );
    }, [users, searchTerm]);

    return (
        <ul>
            {filteredUsers.map(user => (
                <li key={user.id}>{user.name}</li>
            ))}
        </ul>
    );
}
```

## useCallback: Memoize a Function

`useCallback` memoizes a function and only recreates it when dependencies change.

### Syntax

```javascript
const memoizedFn = useCallback(() => {
    doSomething(a, b);
}, [a, b]);
```

### Why Is This Useful?

In JavaScript, every time a function is defined, it's a new reference:

```javascript
const fn1 = () => console.log('hello');
const fn2 = () => console.log('hello');

fn1 === fn2;  // false! They are different functions
```

This causes problems with React.memo and useEffect dependencies.

### Example with React.memo

```javascript
// Memoized child component
const ProductItem = React.memo(function ProductItem({ product, onSelect }) {
    console.log('ProductItem rendered:', product.name);
    return (
        <li onClick={() => onSelect(product.id)}>
            {product.name}
        </li>
    );
});

// Parent component
function ProductList({ products }) {
    const [selected, setSelected] = useState(null);

    // WITHOUT useCallback: new function on every render
    // -> ProductItem re-renders even if product hasn't changed
    const handleSelect = (id) => {
        setSelected(id);
    };

    // WITH useCallback: same function as long as dependencies don't change
    const handleSelectMemo = useCallback((id) => {
        setSelected(id);
    }, []);  // [] because setSelected is stable

    return (
        <ul>
            {products.map(p => (
                <ProductItem
                    key={p.id}
                    product={p}
                    onSelect={handleSelectMemo}
                />
            ))}
        </ul>
    );
}
```

### Example with Dependencies

```javascript
function SearchForm({ onSearch, category }) {
    const [query, setQuery] = useState('');

    // The function depends on category
    const handleSubmit = useCallback(() => {
        onSearch(query, category);
    }, [onSearch, query, category]);

    return (
        <form onSubmit={handleSubmit}>
            <input
                value={query}
                onChange={(e) => setQuery(e.target.value)}
            />
            <button type="submit">Search</button>
        </form>
    );
}
```

## React.memo: Memoize a Component

`React.memo` prevents a component from re-rendering if its props haven't changed.

```javascript
const MyComponent = React.memo(function MyComponent({ name, onClick }) {
    console.log('MyComponent rendered');
    return <button onClick={onClick}>{name}</button>;
});
```

**Important**: React.memo compares props by reference. If a function is recreated on every render, the component will still re-render.

## When to Use useMemo?

**YES:**

- Expensive calculations (sorting, filtering large lists)
- Creating objects passed as props to memoized components
- Calculations whose result is used in multiple places

**NO:**

- Simple calculations (addition, concatenation)
- Components that re-render anyway
- "Just in case" by default

## When to Use useCallback?

**YES:**

- Functions passed to memoized child components (React.memo)
- Functions in useEffect dependencies
- Expensive event handlers

**NO:**

- Functions used only locally
- Child components not memoized
- "For optimization" by default

## Comparison

| Hook | Memoizes | Recalculates if |
| --- | --- | --- |
| useMemo | A value | Dependencies change |
| useCallback | A function | Dependencies change |
| React.memo | A component | Props change |

## Pitfall: Incorrect Dependencies

```javascript
// PROBLEM: items is missing from dependencies
const getTotal = useCallback(() => {
    return items.reduce((sum, item) => sum + item.price, 0);
}, []);  // items should be in the array!

// CORRECT
const getTotal = useCallback(() => {
    return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
```

ESLint with the `eslint-plugin-react-hooks` plugin will warn you about missing dependencies.

## Exercises

### Exercise 1: Optimized Filtered List

Create a product list with a search field. Optimize to not re-filter if the search hasn't changed.

**Solution:**

```javascript
function OptimizedProductList({ products }) {
    const [search, setSearch] = useState('');
    const [sortBy, setSortBy] = useState('name');

    const filteredAndSorted = useMemo(() => {
        console.log('Filtering and sorting...');
        return products
            .filter(p => p.name.toLowerCase().includes(search.toLowerCase()))
            .sort((a, b) => {
                if (sortBy === 'name') return a.name.localeCompare(b.name);
                if (sortBy === 'price') return a.price - b.price;
                return 0;
            });
    }, [products, search, sortBy]);

    return (
        <div>
            <input
                value={search}
                onChange={(e) => setSearch(e.target.value)}
                placeholder="Search..."
            />
            <select value={sortBy} onChange={(e) => setSortBy(e.target.value)}>
                <option value="name">By name</option>
                <option value="price">By price</option>
            </select>
            <ul>
                {filteredAndSorted.map(p => (
                    <li key={p.id}>{p.name} - {p.price}€</li>
                ))}
            </ul>
        </div>
    );
}
```

### Exercise 2: Stable Callback

Create a parent component with multiple child buttons. Optimize so buttons don't re-render unnecessarily.

**Solution:**

```javascript
const Button = React.memo(function Button({ id, onClick, children }) {
    console.log('Button rendered:', id);
    return <button onClick={() => onClick(id)}>{children}</button>;
});

function ButtonGroup() {
    const [clicked, setClicked] = useState(null);

    const handleClick = useCallback((id) => {
        setClicked(id);
        console.log('Button clicked:', id);
    }, []);

    return (
        <div>
            <p>Last click: {clicked}</p>
            <Button id="a" onClick={handleClick}>Button A</Button>
            <Button id="b" onClick={handleClick}>Button B</Button>
            <Button id="c" onClick={handleClick}>Button C</Button>
        </div>
    );
}
```

## Key Points to Remember

1. **useMemo** memoizes a **calculated value**
2. **useCallback** memoizes a **function**
3. **React.memo** memoizes a **component**
4. **Don't optimize prematurely** - measure first
5. **Complete dependencies** - always list all used values

[<- Back to module](/training/module4-react-avance) | [Next Chapter: Custom Hooks ->](/training/module4-react-avance/custom-hooks)
