React Memo Guide with Examples | Refine (2024)

This article was last updated on January 16, 2024 to reflect the latest changes to the React memo API and to provide a more detailed explanation of how React.memo() works.

Introduction

This post introduces the React Memoization Series and demonstrates the usage of the React.memo API. React.memo memoizes a functional component and its props. Doing so helps prevent unnecessary re-renderings that originate from the re-renderings of the component's parent / ancestors.

This is the first post of a three-part series hosted on Refine blog on the use of memoization in React.

The other two posts in the series cover the usage of React useMemo() and useCallback() hooks.

Steps we'll cover in this post:

  • What is Memoization?
  • Why Memoization in React?
    • Excessive Re-rendering Due to Ancestor Re-rendering
    • Expensive Utilities
    • Passing Callbacks to Children
  • Memoization in React
  • About the React Memoization Series
  • Memoizing a Functional Component using React.memo()
    • What is React.memo ?
    • React.memo() - How to Memoize Component Props
    • When to Use React.memo
    • When Not to Use React.memo
    • React.memo: Prop Comparison
    • Using React Memo with Custom Comparators

What is Memoization?

Memoization is an performance optimization technique that allows us to minimize the use of memory and time while executing a resource-intensive function. It works by storing the last computed value or object from the function. Memoization lets us bypass the function's costly computations when the function is called with the same parameters repeatedly.

Why Memoization in React?

Memoization plays a crucial role in enhancing the performance of a React component. It addresses following shortcomings in React:

Excessive Re-rendering Due to Ancestor Re-rendering

React is all about re/rendering components in the virtual DOM prior to updating the actual Document Object Model in the browser. Re-render in an ancestor component, by default, triggers a re-render in a descendent component.

For example, a local state update in a parent component causes it to re-render. This, in turn, causes its children to re-render.

Such behavior in React causes a lot of memory and time to be wasted on useless renderings of the descendent components. Excessive re-renderings, therefore impact a React app's performance negatively.

Expensive Utilities

In addition, resource intensive functions such as utilities used in data processing, transformation and manipulation lower a React app's performance. Functions used for sorting, filtering and mapping traverse large sets of data and therefore slows down an application.

Passing Callbacks to Children

Performance of a React app is also adversely effected due to callback functions passed from a parent component to a child. This happens because a new function object from the callback is created in memory every time the child re-renders. So, multiple copies of the same callback function are spun off in runtime and they consume resources unnecessarily.

Memoization in React

Using memoization the right way in React helps in mitigating these drawbacks and facilitates better use of computing resources in a React app.

Memoization can be used in a number of ways for optimizing the performance of a React app. React components can be memoized to prevent unnecessary component re-renders originating from ancestors participating in the component hierarchy. In functional React, component memoization is done using the React.memo API.

Caching values of expensive utility functions and memoizing callbacks are two common ways of boosting a React app's performance. Caching function values is done using useMemo() hook. And callback functions are memoized with the useCallback() hook.

About the React Memoization Series

The React Memoization Series is a three part guide on how to implement memoization in a React app. Each part demonstrates in the browser console how memoization contributes to performance optimization.

The three parts are:

  1. React Memo Guide with Examples
  2. React useMemo Hook Guide With Examples
  3. Memoization in React - How useCallback Works

In the first post, we implement memoizing a React component with React.memo() and demonstrate how unnecessary re-renders coming from ancestor state updates are prevented. The second post covers how caching the value of an expensive utility function with useMemo stops repetitive invocations of data heavy computations that slow down a React app. In the third part, we get an idea on how memoization of callbacks passed to child components helps reduce application memory consumption.

We will begin with an example that involves memoizing a functional component with React.memo(). In the subsequent posts, we will gradually extend it to include use cases for the useMemo() and useCallback() hooks.

Project Overview

This series is a demo rather than a step-by-step coding tutorial. It is intended to demonstrate how memoization contributes to performance optimization in a React app. We've made the code available here.

All the components have been already coded. We'll be showing how memoization is implemented using React.memo, useMemo() and useCallback APIs by examining relevant code snippets and highlighting lines on the existing components.

We'll follow the impact of memoization mainly from the browser's console.

Setup

In order to properly follow this tutorial, we recommend you run the app in a browser - since we will be visiting the console to investigate the impact of memoization on our React app.

For this to happen, please follow the below steps as outlined here:

  1. Clone this repository.
  2. Open it in your code editor and install the packages:
yarn install
  1. Then run the app:
yarn start
  1. Open Google Chrome and navigate to http://localhost:3000.
  2. Use CTRL + Shift + J on Ubuntu or Command + Option + J on Mac to inspect the webpage and open browser's console.

Investigation

If you look at the project folder in your code editor, you'll find that react-memoization is created using create-react-app.

The app is based on the idea of a list of posts on a blog. There are several components involving a user presented 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 re-render of its descendants.

Let's dig into the components and check out what's happening.

The <App /> Component

To begin with, we have an <App /> component that houses <Blog />.

If we look inside <App />, we can see that we're storing a signedIn state with useState() hook. We also have a toggler function that alters the value of signedIn:

src/components/App.jsx

import { useState } from "react";
import Blog from "./components/Blog";

function App() {
const [signedIn, setSignedIn] = useState(false);
const handleClick = () => setSignedIn(!signedIn);

console.log("Rendering App component");

return (
<main>
<nav>
<button onClick={handleClick}>Sign Out</button>
</nav>
<Blog signedIn={signedIn} setSignedIn={setSignedIn} />
</main>
);
}

export default App;

In the JSX, we pass signedIn to <Blog />.

The <Blog /> Component

Looking inside <Blog />, it fetches a list of posts with a click on the Get Latest Post button and sets the updatedPosts state:

src/components/Blog.jsx

import React, { useEffect, useMemo, 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>
<div>{localTime}</div>
<button onClick={getLatestPosts}>Get Latest Post</button>
<LatestPost signedIn={signedIn} post={sortedPosts[0]} />
<UserPostsIndex signedIn={signedIn} />
</div>
);
};

export default Blog;

We can see that the updatedPosts are sorted with the sortPosts utility and the first item from the sorted array is then passed to <LatestPost /> component along with signedIn.

The <LatestPost /> Component

Then coming to <LatestPost />, it nests the <Post /> component, which we are going to memoize with React.memo().

Let's quickly run through <LatestPost /> to see what it does:

src/components/LatestPost.jsx

import React, { useEffect, useState } from "react";
import Post from "./Post";

const LatestPost = ({ signedIn, post }) => {
const [likesCount, setLikesCount] = useState(null);

useEffect(() => {
const id = setInterval(() => {
setLikesCount((likesCount) => likesCount + 1);
}, 3000);

return () => clearInterval(id);
}, []);

console.log("Rendering LatestPost component");

return (
<div>
{post ? (
<>
<Post signedIn={signedIn} post={post} />
{likesCount && (
<div className="my-1 p-1">
<span>{likesCount} Likes</span>
</div>
)}
</>
) : (
<p>Click on Get Latest Post button</p>
)}
</div>
);
};

export default LatestPost;

We can see that <LatestPost /> changes its local state of likesCount every 3 seconds in the useEffect() hook. Because of this, <LatestPost /> should re-render every 3 seconds. So should <Post /> as a consequence of being a child of <LatestPost />:

The Post /> Component

Let's now focus on <Post />. It receives signedIn and post as props and displays the content of post:

src/components/Post.jsx

import React from "react";

const Post = ({ signedIn, post }) => {
console.log("Rendering Post component");

return (
<div className="">
{post && (
<div className="post p-1">
<h1 className="heading-sm py-1">{post.title}</h1>
<p>{post.body}</p>
</div>
)}
</div>
);
};

export default Post;

Notice we are logging to the console the event when <Post /> gets rendered: console.log('Rendering Post component');

When we check the console, we can expect to see that <Post /> is re-rendered with a change in likesCount from <LatestPost />. This would be happening even though <Post /> does not depend on likesCount.

If we examine closely, we can see that this is indeed the case: we have <Post /> rendering again and again following an interval:

React Memo Guide with Examples | Refine (1)

Notice, rendering <Post /> is accompanied by <LatestPost /> at 3 seconds interval, so it is consistent that <Post />'s re-renders are happening due to likesCount state changes in <LatestPost />. That is, they are coming at 3000ms intervals from <LatestPost />'s useEffect() hook.

All these re-renders are futile for <Post /> and costly for the app. So we are going to prevent them using component memoization.

Memoizing a Functional Component using React.memo()

Now, if we memoize <Post /> with React.memo(), the re-renders should stop.

So, in <Post />, let's update the component export with the highlighted code:

src/components/Post.jsx

const Post = ({ signedIn, post }) => {

console.log('Rendering Post component');

return ( ... );
};

export default React.memo(Post);

Looking at the console, we can see that Post is no longer re-rendered at 3s intervals:

React Memo Guide with Examples | Refine (2)

It is clear that memoizing <Post /> reduces the number of re-renders. In a real app, this is a huge blessing because re-renders due to frequent likes turn out to be very costly for a social media app's performance.

But what exactly happened?

What is React.memo ?

Well, with export default React.memo(Post);, we produced a new component that re-renders only when its props and internal state is changed.

React.memo() is a Higher Order Component (HOC) that memoizes the passed in component along with the value of its props. Doing so helps in optimizing its performance by preventing unnecessary re-renders due to changes it does not depend on, e.g. the unrelated state changes in ancestor components.

React.memo does this by memoizing the component function itself and the accepted props. When the values of the props change, the component re-renders.

React.memo() - How to Memoize Component Props

We can see that <Post /> receives signedIn and post props.

Now, unlike with likesCount, <Post /> depends on signIn and post. And React memo caches these props and checks for incoming changes in them. Incoming changes to them triggers a re-render. So, altering any of signedIn or post re-renders Post.

If we look back inside <App />, we see that signedIn originated from there and gets relayed via <Blog /> and <LatestPost /> to <Post /> as props. We have a button in the navbar that toggles the value of signedIn:

<nav className="navbar">
<button className="btn btn-danger" onClick={handleClick}>
Sign Out
</button>
</nav>

In the browser, let's try toggling its value to see the effect on memoized <Post />.

Add the following console log statement to <Post /> in order to log the value of signedIn to the console:

console.log(signedIn);

When we click on the Sign Out button in the navbar, we can see in the console that <Post /> re-renders after <LatestPost />:

React Memo Guide with Examples | Refine (3)

This is now because React memo caches the props passed to the component and checks for incoming changes. Notice the Boolean value of signedIn printed to the console. A change in signedIn's state renews the memoization and a re-render of the component is triggered.

When to Use React.memo

This is actually what we want. Because we don't want <Post /> to re-render when we don't need it to, and we want to re-render it when we need it to.

If value of signedIn never changed, we know <Post /> will never be re-rendered because of signedIn. In that case, caching signedIn doesn't do us any favor.

So, typically we should use React.memo when we want to prevent re-renderings due to state changes that do not concern our component and only allow re-renderings due to prop changes that happen often or are driven by an event.

When Not to Use React.memo

In our example, had we resorted to React.memo() solely to retain the value of signedIn and not to prevent re-renders due to changes in likesCount or post, we would not get much performance benefit.

Instead, we would be bringing the comparison function into the scene for no reason, which adds to the performance cost. So, it is not recommended to memoize a component if its prop values don't change often.

It is therefore important to figure out the performance gains by measuring and analyzing runtime performance using browser utilities like Chrome DevTools.

React.memo: Prop Comparison

React memo checks for changes between the previous and current values for a given prop passed to the component. The default function carries out a shallow comparison on each passed in prop. It checks for equality of incoming values with the existing ones.

In our React.memo(Post) memo, the current states of signedIn and post are checked for equality to their incoming states. If both values for each prop are equal, the memoized value is retained and re-render prevented. If they are not equal, the new value is cached and <Post /> re-renders.

Using React Memo with Custom Comparators

It is possible to customize the comparison by passing in a comparator function to the React.memo() HOC as a second argument:

React.memo(Post, customComparator);

For example, we can specify dependencies for React.memo() and choose to compare only the props we want to:

src/components/Post.jsx

import React from "react";

const Post = ({ signedIn, post }) => {
console.log("Rendering Post component");

return ( ... );
};

const customComparator = (prevProps, nextProps) => {
return nextProps.post === prevProps.post;
};

export default React.memo(Post, customComparator);

Here, we are omitting signedIn from the comparison by including only post. Now, if we click on Sign Out button, Post is not being re-rendered:

React Memo Guide with Examples | Refine (4)

This is because, our customComparator checks for equality of incoming values of only post and excludes signedIn from the comparison.

Summary

In this post, we acknowledged what memoization is and why it is important in React. We learned about the use of React.memo(), useMemo and useCallback APIs for implementing memoization in a React app.

By investigating a demo blog post app, we observed in the browser console that React.memo() is very useful in preventing unnecessary, frequent re-renders of a component due to ancestor state changes that it does not depend on. A good example involves a component that accepts props whose values change often and/or on demand. With a React.memo custom comparator function, we can choose to specify only the props we want to track for triggering a re-render of our component.

In the next article, we will turn our attention to the <Blog /> component and memoize a sorting function with useMemo() hook.

Live Example

Run on your local

npm create refine-app@latest -- --example blog-react-memoization-memo
React Memo Guide with Examples | Refine (2024)
Top Articles
How to Find the Greatest Common Factor (GCF) - dummies
Civil Rights Department Reaches Settlement Ending Novato Apartment Complex Ban on Section 8 Vouchers
St Thomas Usvi Craigslist
Fort Morgan Hometown Takeover Map
Lexi Vonn
Mountain Dew Bennington Pontoon
Visitor Information | Medical Center
Occupational therapist
Atvs For Sale By Owner Craigslist
Rek Funerals
Wells Fargo Careers Log In
Dee Dee Blanchard Crime Scene Photos
Flights to Miami (MIA)
Palace Pizza Joplin
Gt Transfer Equivalency
How To Delete Bravodate Account
Weekly Math Review Q4 3
Helloid Worthington Login
Synq3 Reviews
Walthampatch
Vcuapi
The Banshees Of Inisherin Showtimes Near Regal Thornton Place
Classic | Cyclone RakeAmerica's #1 Lawn and Leaf Vacuum
Lista trofeów | Jedi Upadły Zakon / Fallen Order - Star Wars Jedi Fallen Order - poradnik do gry | GRYOnline.pl
Doki The Banker
Sec Baseball Tournament Score
Regina Perrow
Catchvideo Chrome Extension
Xpanas Indo
Mchoul Funeral Home Of Fishkill Inc. Services
24 Hour Drive Thru Car Wash Near Me
Osrs Important Letter
Uno Fall 2023 Calendar
Craigslist Scottsdale Arizona Cars
Promatch Parts
Calculator Souo
Worlds Hardest Game Tyrone
The Land Book 9 Release Date 2023
Autozone Locations Near Me
Myql Loan Login
Myanswers Com Abc Resources
Captain Billy's Whiz Bang, Vol 1, No. 11, August, 1920&#10;America's Magazine of Wit, Humor and Filosophy
Trap Candy Strain Leafly
Union Corners Obgyn
The All-New MyUMobile App - Support | U Mobile
Engr 2300 Osu
manhattan cars & trucks - by owner - craigslist
Tyco Forums
bot .com Project by super soph
Haunted Mansion Showtimes Near Millstone 14
Divisadero Florist
Latest Posts
Article information

Author: Mrs. Angelic Larkin

Last Updated:

Views: 6199

Rating: 4.7 / 5 (67 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Mrs. Angelic Larkin

Birthday: 1992-06-28

Address: Apt. 413 8275 Mueller Overpass, South Magnolia, IA 99527-6023

Phone: +6824704719725

Job: District Real-Estate Facilitator

Hobby: Letterboxing, Vacation, Poi, Homebrewing, Mountain biking, Slacklining, Cabaret

Introduction: My name is Mrs. Angelic Larkin, I am a cute, charming, funny, determined, inexpensive, joyous, cheerful person who loves writing and wants to share my knowledge and understanding with you.