DEV Community

Cover image for useMemo in React: Optimizing Performance by Memoizing Values
Shefali
Shefali

Posted on • Originally published at shefali.dev

useMemo in React: Optimizing Performance by Memoizing Values

React re-renders can slow down your app, especially with expensive calculations. The useMemo hook helps optimize performance by memoizing values, ensuring that unnecessary computations are avoided.

In this post, you’ll learn about the useMemo hook, including its syntax and how it works with examples.

Before we get started, don’t forget to subscribe to my newsletter!
Get the latest tips, tools, and resources to level up your web development skills delivered straight to your inbox. Subscribe here!

Now, let’s jump right into it!

What is useMemo?

useMemo is a React Hook used to optimize expensive calculations. This memoizes the computed values to avoid calculation on every render.

This is useful for improving performance, especially with large lists, heavy computations, or unnecessary renders.

For example, imagine you have a large list that needs filtering every time a user types in a search box. Without optimization, performance slows down as filtering runs on every render. useMemo helps solve this problem by caching the filtered results.

If an expensive calculation is happening repeatedly without any state change, then you can optimize this using useMemo.


Basic Syntax of useMemo

Before using useMemo, you need to import it from React:

import { useMemo } from "react";
Enter fullscreen mode Exit fullscreen mode

Syntax:

const memoizedValue = useMemo(() => computeExpensiveValue(dependencies), [dependencies]);
Enter fullscreen mode Exit fullscreen mode

Here,

  • useMemo accepts a function that performs expensive calculations.
  • useMemo caches the result of the expensive function.
  • This will recalculate only when dependencies change.
  • If dependencies don’t change, then it will return the memoized value without computation.

Without useMemo (Unoptimized Code)

If you do expensive calculations on each render, then React will do unnecessary re-calculations, which is not good for performance.

For example:

import { useState } from "react";

function ExpensiveComponent({ numbers }) {
  function sum(numbers) {
    console.log("Calculating sum...");
    return numbers.reduce((acc, num) => acc + num, 0);
  }

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

  const total = sum(numbers); // This will calculate on every render

  return (
    <div>
      <p>Sum: {total}</p>
      <button onClick={() => setCount(count + 1)}>Re-render: {count}</button>
    </div>
  );
}

export default function App() {
  return <ExpensiveComponent numbers={[1, 2, 3, 4, 5]} />;
}
Enter fullscreen mode Exit fullscreen mode

How does it work?

  • The useState hook is imported to manage state inside the component.
  • The ExpensiveComponent function takes a numbers prop, which is an array of numbers.
  • Inside the component, a function sum( ) calculates the sum of the numbers using the reduce() method and logs “Calculating sum…” to track how often it runs.
  • A state variable count is initialized with 0, and the setCount function updates it when the button is clicked.
  • The issue arises because the sum( ) function is called on every render, even when the numbers prop hasn’t changed, leading to unnecessary recalculations.
  • The component displays the sum and a button that increments the count, triggering a re-render.
  • The App component renders ExpensiveComponent with a predefined list of numbers.

Problem with This Code:

Every time the button is clicked, setCount updates the state. React re-renders the component, and the sum( ) is called again, even though the numbers didn’t change. This is inefficient, especially for complex calculations.

Solution:

Using the useMemo hook can optimize this by memoizing the computed sum, ensuring it only recalculates when the numbers prop changes. This prevents unnecessary executions and improves performance.

With useMemo (Optimized Code)

You can use useMemo in the above example to avoid unnecessary calculations.

import { useState, useMemo } from "react";

function ExpensiveComponent({ numbers }) {
  function sum(numbers) {
    console.log("Calculating sum...");
    return numbers.reduce((acc, num) => acc + num, 0);
  }

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

  const total = useMemo(() => sum(numbers), [numbers]); // Recalculates only when `numbers` change

  return (
    <div>
      <p>Sum: {total}</p>
      <button onClick={() => setCount(count + 1)}>Re-render: {count}</button>
    </div>
  );
}

export default function App() {
  return <ExpensiveComponent numbers={[1, 2, 3, 4, 5]} />;
}
Enter fullscreen mode Exit fullscreen mode

How does it work?

  • The useState and useMemo hooks are imported from React.
  • The ExpensiveComponent function receives a numbers prop, which is an array.
  • Inside the component, the sum( ) function calculates the sum of the numbers and logs “Calculating sum…” to track executions.
  • A state variable count is initialized with 0, and setCount updates it when the button is clicked.
  • The useMemo hook is used to memoize the result of the sum(numbers). This ensures the sum is only recalculated when numbers change, preventing unnecessary computations on re-renders.
  • The component displays the sum and a button that increments the count, triggering a re-render.
  • The App component renders ExpensiveComponent with a predefined list of numbers.

Why is this better?

Without useMemo, sum( ) runs on every re-render, even if the numbers stay the same. With useMemo, sum( ) runs only when numbers change, improving performance.

useMemo in Large List Filtering

You can optimize the sorting or filtering of a large list using the useMemo hook.

Without useMemo (Unoptimized List Filtering)

import { useState } from "react";

function List({ items, filterText }) {
  const filteredItems = items.filter(item => item.includes(filterText)); // Filtering will happen on every render

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

export default function App() {
  const [filterText, setFilterText] = useState("");

  return (
    <div>
      <input onChange={(e) => setFilterText(e.target.value)} />
      <List items={["apple", "banana", "grape", "orange", "pineapple"]} filterText={filterText} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

What does this code do?

  • It renders a list of fruits (items) and filters them based on the text entered in the input field (filterText).
  • Whenever the user types in the input, the list updates dynamically to show only the matching items.

How does it work?

State Management (useState):

  • filterText is a state variable initialized as an empty string ("").
  • setFilterText updates filterText whenever the user types in the input field.

Filtering Logic (Inside List Component):

  • The filteredItems array is created using items.filter(item => item.includes(filterText)).
  • This means if filterText = “ap”, it will return [“apple”, “grape”, “pineapple”].

Rendering the List (map()):

  • filteredItems.map(item => <li key={item}>{item}</li>) maps each filtered item into a <li> inside <ul>.

Re-rendering Issue:

  • Every time filterText changes, the App component re-renders, causing List to re-run filter().
  • This is inefficient for large lists.

With useMemo (Optimized List Filtering)

import { useState, useMemo } from "react";

function List({ items, filterText }) {
  const filteredItems = useMemo(() => {
    console.log("Filtering list...");
    return items.filter(item => item.includes(filterText));
  }, [items, filterText]); // Filter only when `items` or `filterText` changes

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

export default function App() {
  const [filterText, setFilterText] = useState("");

  return (
    <div>
      <input onChange={(e) => setFilterText(e.target.value)} />
      <List items={["apple", "banana", "grape", "orange", "pineapple"]} filterText={filterText} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

What does this code do?

  • It filters a list of fruits (items) based on user input (filterText).
  • Uses useMemo to optimize filtering and prevent unnecessary recalculations.

How does it work?

State Management (useState):

  • filterText is stored using useState.
  • When the user types in the input field, setFilterText updates the state.

Filtering Optimization (useMemo):

  • filteredItems is memoized using useMemo(), ensuring filtering happens only when items or filterText changes.
  • Without useMemo, filtering would run on every render, even if filterText stays the same.

Console Logging for Debugging:

  • “Filtering list…” logs when filtering runs.
  • If the input remains unchanged, filtering won’t run, proving memoization works.

Rendering the List (map()):

  • The filteredItems array is mapped into <li> elements inside <ul>.

Why is this better than the previous version?

Without useMemo, filtering runs on every render. With useMemo, it runs only when necessary, improving performance, especially for large lists.


What is the difference between useCallback and useMemo?

Feature useCallback useMemo
Purpose Function memoization Value memoization
Returns Memoized function reference Memoized computed value
Use Case When unnecessary function recreation happening When expensive calculation is happening repeatedly

In simple:

  • If you need to memoize a function, use useCallback.
  • If you need to memoize a computed value or expensive calculation, use useMemo.

🎯Wrapping Up

That’s all for today!

For paid collaboration connect with me at : connect@shefali.dev

I hope this post helps you.

If you found this post helpful, here’s how you can support my work:
Buy me a coffee – Every little contribution keeps me motivated!
📩 Subscribe to my newsletter – Get the latest tech tips, tools & resources.
𝕏 Follow me on X (Twitter) – I share daily web development tips & insights.

Keep coding & happy learning!

Top comments (0)