Optimizing Performance: React.memo vs. useMemo

Published on 1.8.2019

As your React applications grow in complexity, performance becomes an important consideration. React makes it easy to build dynamic UIs, but in larger applications, unnecessary re-renders can slow down performance. In this post, we’ll explore two React optimizations — React.memo and useMemo — and discuss how and when to use each to prevent unnecessary re-renders and optimize your app’s performance.


Understanding Re-renders in React

Before we dive into optimizations, let’s understand what causes re-renders. React re-renders a component whenever its state or props change. While re-renders are necessary to keep the UI up to date, they can also become expensive if not managed properly, especially with complex or large components.

When your component re-renders more than necessary, it can lead to performance bottlenecks. For instance, a component that receives unchanged props or doesn’t need to update might still re-render, wasting unnecessary resources.

Fortunately, React provides tools like React.memo and useMemo to help us avoid these performance pitfalls.


React.memo – Preventing Unnecessary Re-renders of Functional Components

React.memo is a higher-order component that memoizes a component, preventing it from re-rendering unless its props change. If the props remain the same between renders, React will skip the re-render and reuse the previous output.

It’s particularly useful when you have a functional component that receives props but doesn’t rely on internal state or context that changes frequently.

Example: Optimizing a List Component with React.memo

Let’s say we have a simple list of items, and each list item is a functional component. Without optimization, if the parent component re-renders, all of the list items would re-render, even if their props haven’t changed.

import React, { useState } from 'react';

const ListItem = ({ item }) => {
  console.log('Rendering ListItem:', item);
  return <div>{item}</div>;
};

const MemoizedListItem = React.memo(ListItem);

const List = () => {
  const [count, setCount] = useState(0);
  const items = ['Apple', 'Banana', 'Cherry'];

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <div>Count: {count}</div>
      <div>
        {items.map((item, index) => (
          <MemoizedListItem key={index} item={item} />
        ))}
      </div>
    </div>
  );
};

export default List;

Explanation:

  1. ListItem Component: This component simply renders an item from the list. Without optimization, this component would re-render every time the parent List component re-renders.
  2. React.memo: We wrap the ListItem component in React.memo to prevent unnecessary re-renders. Now, even if the List component re-renders (e.g., due to a state change), ListItem will only re-render if its item prop changes.
  3. Memoization Effect: We’ve added a button that increments the count state. Although the count state changes, the ListItem components won’t re-render because their props (the list items) haven’t changed.

Key Takeaway: Use React.memo to optimize functional components that only depend on props and avoid re-rendering when props remain unchanged.


useMemo – Memoizing Expensive Calculations

useMemo is a hook that memoizes the result of an expensive calculation, ensuring it’s only recalculated when its dependencies change. It’s typically used to avoid re-computing derived data or expensive calculations unnecessarily on each render.

Example: Optimizing Expensive Calculations with useMemo

Imagine you have a list of numbers and need to filter out even numbers. If the list of numbers is large, it can be an expensive operation. Let’s use useMemo to optimize this calculation.

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

const ExpensiveFilter = () => {
  const [filterText, setFilterText] = useState('');
  const [items, setItems] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

  // Memoizing the filtered list to avoid recalculating it on each render
  const filteredItems = useMemo(() => {
    console.log('Filtering items...');
    return items.filter((item) => item.toString().includes(filterText));
  }, [filterText, items]);

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

export default ExpensiveFilter;

Explanation:

  1. useMemo: We use useMemo to memoize the result of the filter operation. The filter will only be re-run if either filterText or items changes. This prevents the filtering logic from running on every re-render of the component.
  2. Performance Gain: Without useMemo, the filtering logic would be re-executed on every keystroke, which could be expensive for large lists. With useMemo, it’s only recalculated when necessary.

Key Takeaway: Use useMemo when you have an expensive calculation or transformation that you want to avoid repeating unnecessarily on every render.


When to Use React.memo vs. useMemo

  • React.memo: Use React.memo to prevent unnecessary re-renders of a component when its props haven’t changed. It’s ideal for optimizing child components that receive props from a parent component.
  • useMemo: Use useMemo to optimize expensive calculations that depend on specific variables. It helps prevent recalculating values or running expensive functions on every render.

When Not to Use useMemo and React.memo

While React.memo and useMemo are powerful tools, they’re not always necessary. Using them prematurely can lead to unnecessary complexity. As a general rule:

  • Don’t use them for trivial optimizations. React’s built-in rendering optimizations (like re-rendering only when necessary) are already pretty efficient for most cases.
  • Measure first: If you notice performance issues in a specific area of your app, measure the impact of optimizations like React.memo and useMemo to ensure they are worth the trade-off.

Conclusion

Optimizing React applications is all about avoiding unnecessary work. Both React.memo and useMemo provide useful ways to prevent unnecessary re-renders and recalculations, improving performance.

  • Use React.memo to optimize functional components that only depend on props.
  • Use useMemo to memoize expensive calculations that depend on specific values or arrays.

By applying these optimizations thoughtfully, you can ensure your React applications stay fast and responsive, even as they grow in complexity.

Happy coding!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *