React useMemo() guide with examples

React useMemo() guide with examples

Author: Abdullah Numan

Introduction

This post is about how to use the useMemo() hook in React.

useMemo() is a function that returns a memoized value of a passed in resource-intensive function. It is very useful in optimizing the performance of a React component by eliminating repeating heavy computations.

In this post, we dive into the details of the useMemo hook with an extension of the example demonstrated in the previous post titled Memoization using React memo.

Steps we'll cover:

Project Content Overview

The example app is based on the idea of a list of posts on a blog. There are several components involving a user seeing the latest posts and a list of the user's posts. Allow yourself some time to understand the components individually, their relationships, their state changes, and how props are passed through. It is crucial to pay close attention to how the change of a parent's state triggers the re-render of its descendants.

The discussion of this article is focused on optimizing performance by memoizing the value of resource-intensive functions, such as a sorting function. In React, we do this with the useMemo() hook.

Resource Intensive Functions

We're going to jump back to the <Blog /> component for this example:

// src/components/Blog.jsx

import React, { useEffect, useState } from 'react';
import fetchUpdatedPosts from '../fetch/fetchUpdatedPosts';
import allPosts from './../data/allPosts.json';
import sortPosts from '../utils/sortPosts';
import LatestPost from './LatestPost';
import UserPostsIndex from './UserPostsIndex';

const Blog = ({ signedIn }) => {
  const [updatedPosts, setUpdatedPosts] = useState(allPosts);
  const [localTime, setLocalTime] = useState(new Date().toLocaleTimeString());

  const getLatestPosts = () => {
    const posts = fetchUpdatedPosts();
    setUpdatedPosts(posts);
  };

 const sortedPosts = sortPosts(updatedPosts);

  useEffect(
    () => {
      const id = setInterval(
        () => setLocalTime(new Date().toLocaleTimeString()),
        1000
      );
      return () => clearInterval(id);
    },
    []
  );

  console.log('Rendering Blog component');

  return (
    <div className="container">
      <h1>Memoization in React</h1>
      <div>
        <div>
          { /* More JSX code here... */ }
          <LatestPost signedIn={signedIn} post={sortedPosts[0]} />
        </div>
        <UserPostsIndex signedIn={signedIn}/>
      </div>
    </div>
  );
};

export default React.memo(Blog);

We'd like to focus particularly on the sortPosts() utility function which can get expensive if passed a long array of posts.

At the moment, we are only sorting 101 items returned from fetchUpdatedPosts(), but in an actual application, the number can be much higher and consume resources at scale. Thus it is an expensive function.

If we look inside the useEffect() hook, we are updating the locale time string and storing it in localTime for our clock. localTime updates every second, and at each state, change triggers a re-render of <Blog />. The clock does not represent a genuine UI feature for us here, but it is there to make a point about how frequent re-renders complicate things with expensive utility functions.

Our sortPosts() logs Sorting posts... to the console and returns a sorted array from the passed in an array:

// src/utils/sortPosts

const sortPosts = posts => {
  console.log('Sorting posts...');
  return posts.sort((a, b) => b.id - a.id);
};

export default sortPosts;

If we look at the console, we see that Sorting posts... is being looged at 1000ms intervals, i.e. with the tick of our clock:

usememo-1.png

This shows sortPosts() is called at every re-render of <Blog />. An expensive function, invoked every second for no obvious reason, is too much of an ask from the app. We don't want sortPosts() to be called if updatedPosts is not changed.

Enter useMemo()

useMemo() helps us memoize the value of sortPosts() when updatedPosts doesn't change. Let's use the memoized function:

// src/components/Blog.jsx

-- const sortedPosts = sortPosts(updatedPosts);

++ const sortedPosts = useMemo(() => sortPosts(updatedPosts), [updatedPosts]);

Checking our console, we can see that Sorting posts... has been logged only once, indicating only one invocation of sortPosts():

usememo-2.png

This gives us a huge performance gain.

useMemo Dependencies

Notice the dependency of useMemo() as the second argument, updatedPosts. We are asking the hook to renew the memo when updatedPosts changes. Let's try to change the value of updatedPosts:

In the <Blog/> component, we have a Get Latest Post button, which is used to fetch latest posts on demand. Every time Get Latest Post button is clicked, updatedPosts is updated with the invocation of getLatestPosts().

If the state of updatedPosts is changed, a re-render of <Blog /> is triggered, which leads to a call to sortPosts() with the new value of updatedPosts passed in.

If we check our console while clicking the button, we can clearly see Sorting posts... being logged for each click:

usememo-3.png

Info: It is important to notice that, if we remove the dependency from useMemo(), sortPosts() will not be invoked when updatedPosts change:

  const sortedPosts = useMemo(() => sortPosts(updatedPosts), []);

There is no sorting going on when we need it:

usememo-4.png


It is also important to know that useMemo returns a value, as opposed to a function. This is what differentiates it from the useCallback() hook, which returns a memoized function. So, useMemo() is preferred for memoizing a value rather than a callback function.

Conclusion

In this article, we looked into the use of useMemo() hook and found out it plays a crucial role in optimizing the performance of our app by memoizing an expensive utility function. We saw that it is important to specify the dependency of useMemo so that the memo is renewed when the state of dependency changes.

In the next post, we will demonstrate the use of useCallback() hook.

Live StackBlitz Example


Build your React-based CRUD applications without constraints

Low-code React frameworks are great for gaining development speed, but they often fall short of flexibility if you need extensive styling and customization for your project.

Check out refine, if you are interested in a headless framework you can use with any custom design or UI-Kit for 100% control over styling.


refine is an open-source React-based framework for building CRUD applications without constraints. It can speed up your development time up to 3X without compromising freedom on styling, customization, and project workflow.

refine is headless by design and it connects 30+ backend services out-of-the-box, including custom REST and GraphQL API’s.

Visit refine GitHub repository for more information, demos, tutorials, and example projects.