REACT useMemo

The useMemo hook in React is used to memoize values or expensive computations so that they are only recalculated when their dependencies change. This helps in optimizing performance by avoiding unnecessary re-computation during every render.

When to Use useMemo

  • Performance Optimization: To prevent recalculating expensive computations or derived values on every render.
  • Stable Value References: When you want to ensure that a value remains stable (doesn’t change unnecessarily) and avoid triggering re-renders in child components.
  • Reducing Computational Overhead: To optimize resource-intensive calculations in your app.

Syntax

const memoizedValue = useMemo(() => computeFunction(), [dependencies]);

Try It Now

  • computeFunction: The function that performs the expensive computation.
  • dependencies: An array of values that the computation depends on. The function is only re-executed when one of these values changes.

Basic Example

Without useMemo (Recomputes Every Render)

import React, { useState } from "react";

function ExpensiveComponent() {
    const [count, setCount] = useState(0);

    const expensiveComputation = () => {
        console.log("Computing...");
        let sum = 0;
        for (let i = 0; i < 1e7; i++) {
            sum += i;
        }
        return sum;
    };

    const computedValue = expensiveComputation();

    return (
        <div>
            <h1>Count: {count}</h1>
            <p>Computed Value: {computedValue}</p>
            <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
        </div>
    );
}

export default ExpensiveComponent;

Try It Now

In this example, expensiveComputation runs every time the component renders, even if count hasn’t changed, wasting resources.

With useMemo (Efficient Computation)

import React, { useState, useMemo } from "react";

function ExpensiveComponent() {
    const [count, setCount] = useState(0);

    const computedValue = useMemo(() => {
        console.log("Computing...");
        let sum = 0;
        for (let i = 0; i < 1e7; i++) {
            sum += i;
        }
        return sum;
    }, []); // Empty dependency array means computation runs only once.

    return (
        <div>
            <h1>Count: {count}</h1>
            <p>Computed Value: {computedValue}</p>
            <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
        </div>
    );
}

export default ExpensiveComponent;

Try It Now

In this version, the expensive computation only runs once when the component is first rendered, saving performance resources.

Example with Dependencies

If the computation depends on some state or props, include them in the dependency array.

import React, { useState, useMemo } from "react";

function ExpensiveComponent() {
    const [count, setCount] = useState(0);
    const [multiplier, setMultiplier] = useState(1);

    const computedValue = useMemo(() => {
        console.log("Recomputing...");
        return count * multiplier;
    }, [count, multiplier]); // Recomputes only when count or multiplier changes.

    return (
        <div>
            <h1>Count: {count}</h1>
            <h1>Multiplier: {multiplier}</h1>
            <p>Computed Value: {computedValue}</p>
            <button onClick={() => setCount((prev) => prev + 1)}>Increment Count</button>
            <button onClick={() => setMultiplier((prev) => prev + 1)}>Increment Multiplier</button>
        </div>
    );
}

export default ExpensiveComponent;

Try It Now

In this case, computedValue is recalculated only when either count or multiplier changes.

Real-World Example: Optimizing Lists

useMemo can optimize rendering lists by memoizing derived data, like filtered or sorted arrays.

Without useMemo:

import React, { useState } from "react";

function App() {
    const [filter, setFilter] = useState("");
    const items = ["Apple", "Banana", "Orange", "Mango", "Pineapple"];

    const filteredItems = items.filter((item) => item.includes(filter));

    return (
        <div>
            <input
                type="text"
                placeholder="Filter items"
                value={filter}
                onChange={(e) => setFilter(e.target.value)}
            />
            <ul>
                {filteredItems.map((item) => (
                    <li key={item}>{item}</li>
                ))}
            </ul>
        </div>
    );
}

export default App;

Try It Now

Here, the filtering operation is performed on every render, even if filter hasn’t changed.

With useMemo:

import React, { useState, useMemo } from "react";

function App() {
    const [filter, setFilter] = useState("");
    const items = ["Apple", "Banana", "Orange", "Mango", "Pineapple"];

    const filteredItems = useMemo(() => {
        console.log("Filtering...");
        return items.filter((item) => item.includes(filter));
    }, [filter]); // Recomputes only when filter changes.

    return (
        <div>
            <input
                type="text"
                placeholder="Filter items"
                value={filter}
                onChange={(e) => setFilter(e.target.value)}
            />
            <ul>
                {filteredItems.map((item) => (
                    <li key={item}>{item}</li>
                ))}
            </ul>
        </div>
    );
}

export default App;

Try It Now

With useMemo, the filtering operation is only recomputed when the filter value changes, improving performance for large lists.

Key Points About useMemo

  1. Memoizes Computation Results: Ensures the computed value is not recalculated unless its dependencies change.
  2. Dependency Array: If omitted, the function will recompute on every render (not recommended). Always provide the correct dependencies.
  3. Optimization Tool: Use it for expensive computations or derived values, not for trivial values.

Common Mistakes

  1. Overusing useMemo: Avoid using it for lightweight computations as it adds unnecessary complexity.
  2. Ignoring Dependencies: Always include all required dependencies in the dependency array.
  3. Premature Optimization: Use useMemo only when you’ve identified performance bottlenecks.

Key Takeaways

  • Use useMemo to memoize expensive computations or derived values.
  • It’s particularly useful in optimizing large datasets, filtering, sorting, or any heavy computations.
  • Always include accurate dependencies in the dependency array to ensure proper behavior.
  • Use it judiciously to enhance performance without complicating your code unnecessarily.

By using useMemo, you can optimize resource-heavy components in your React application and ensure a smooth user experience!