Skip to main content
Post image
· 6 min read

React useMemo hook guide with examples

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.

Here you can find the example app's live code

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 logged at 1000ms intervals, i.e. with the tick of our clock:

usememo1

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():

usememo2

This gives us a huge performance gain.


discord banner

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:


usememo3

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:

usememo4


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.


discord banner

Example

RUN IN YOUR LOCAL
npm create refine-app@latest -- --example blog-react-memoization-memo

Tags:

Related Articles

Frontend developer
React Hook Form Validation with Complete Examples
· 18 min read
Frontend Developer
Let's Build an App to Manage Your Hackathons with Refine
· 4 min read
Web Developer
How to Build a React Admin Panel with Mantine and Strapi
· 23 min read

From Same Author

Fullstack Developer
Error Handling With try, catch and finally Blocks in JavaScript
· 11 min read
Fullstack Developer
How to Use JavaScript Slice Method
· 9 min read
Fullstack Developer
Audit Log With refine
· 16 min read