Dec 29, 2021
React ·10 min read
Introduction
Often times as a React developer we need to focus on the application’s optimization and performance improvements. There are multiple ways you can archive a blazing-fast React app performance from third-party tools to native features.
One of the most commonly used techniques is to memoize the code we write for a React component and React Memo allows us to do just that! When applied correctly, you get no unnecessary re-renders thereby improving the responsiveness of our app.
In this article, you will get to know everything you need to know about React Memo with some examples and demos included.
What is React Memo?
React Memo is a Higher Order Component (HOC) which itself wraps around a component to memoize the rendered output and skips unnecessary renderings.
The component around which it’s used will generate a memoized or an optimal version of it to speed up the render process. As you can see by now, it’s all done to improve the overall stability and rendering performance of your React application.
Also, it allows the component to check the old props against new ones to see if the value has changed or not.
Let’s get to know something more about it in the following points:
useState
, useReducer
, or useContext
then, it will still re-render whenever the state or the context of the component changes.
Where to use React Memo?
It’s very easy to start using this memoization technique to a majority of your components in the application. But we should know beforehand that this method only exists as a performance optimization.
Here are some important points to note on where to use them:
Now let’s take a closer look at point number 3 above with an example.
This is one of the best use cases for React Memo. Usually, in your React apps, you can see that a situation occurs that makes a component render with the same props being forced to render by a parent component.
For example, below you can see a parent component called SongsViews
which essentially displays the number of views for a song in an application:
function SongsViews({ title, genre, viewCount }) {
return (
<div>
<Song title={title} genre={genre} />Views: {viewCount}
</div>
);
}
Make sure you notice that:
title
, genre
, and viewCount
.
In a practical sense, here’s how it will render:
// Initial render
<SongsViews
viewCount={0}
title="My Song"
genre="Pop"
/>
// After some seconds, views is 40
<SongsViews
viewCount={40}
title="My Song"
genre="Pop"
/>
// After 10 minutes, views is 250
<SongsViews
viewCount={250}
title="My Song"
genre="Pop"
/>
As you can see, whenever the viewCount
prop is updated with a new value, the parent component SongViews
renders. With this, the child component of Song
is also triggered to render even if the other two props of title
and genre
remain the same throughout!
Hence, here we get a perfect case to apply some memoization on the child Song
component so that useless re-rendering is prevented thereby improving the overall performance of the app.
Where NOT to use React Memo?
Now that we know when we would be using React Memo, we should also consider the case where we should avoid using it without any context or overusing it.
In a nutshell, you should not use React Memo in all React components that implement the shouldComponentUpdate()
method. This is because, by default, it returns true always. The render change that occurs by using React Memo is the same implementation of the shouldComponentUpdate()
method which essentially does the shallow comparison of the state and the props.
Other than that, these two cases should also be considered:
PureComponent
or implement shouldComponentUpdate()
method.
Use React Memo in your application
Now that you know all about React Memo, let’s dive into creating a React application that actually uses it. But first, take a look at its syntax:
const MyComponent = React.memo(function MyComponent(props) {
/* render using props */
});
As you can see, we wrap the component to memoize with React.memo()
. Now let’s make a quick demo app to show the use case of memoization in React.
Step 1: Create a new React app
Make sure you have Node.js installed on your system and then run the following commands:
npx create-react-app react-memo-demo
cd react-memo-demo
npm start
This will bootstrap a new React application and run the default app on http://localhost:3000/ in your default browser thanks to the Create React App tool.
Now let’s open the App.js file and remove all the code we have here. We will start coding from scratch a very basic to-do list app.
Step 2: Make the app interface
Our app will have:
Here’s the JSX code we need to return for the above elements:
return (
<div>
<input type="text" value={text} onChange={handleText} />
<button type="button" onClick={handleAddTodo}>
<span role="img" aria-label="add emojie">
➕
</span>
Add todo
</button>
<Todo list={todo} />
</div>
);
As you can see we also added necessary attributes to the input
field with value
and onChange
which gets the text value typed by the user and change event(s) occurring on the input field respectively.
For the button
, we simply have an onClick
handler attached to it which we call handleAddTodo
.
Lastly to render the list of items we don’t use the unordered list tag here as we are using a component called <Todo />
to get the list items by passing the list attribute to it.
Step 3: Create default state, handlers, and components
To handle the state of our app we will be using the useState()
Hook here. Let’s make a functional component called App()
which uses useState()
to initialize our app with two default values of to-do items “Shop groceries 🛒” and “Do yoga 🧘” as follows:
const [todo, setTodo] = React.useState([
{ title: "Shop groceries 🛒" },
{ title: "Do yoga 🧘" }
]);
Also, let’s use useState()
again to initialize the text variable (which gets the actual value) for the input field as:
const [text, setText] = React.useState("");
Next, we got our two handler functions. Let’s deal with them. The first one is handleText()
which simply sets the value present in the input field:
const handleText = (e) => {
setText(e.target.value);
};
The second one is handleAddTodo()
which will be used to combine the array value (with concat()
) and put it as a title
text string value on the list:
const handleAddTodo = () => {
setTodo(todo.concat({ title: text }));
};
We have the <Todo />
component ready. Let’s pass on the list
param to it and return an <ul>
item which maps over the individual item
to render a new <TodoItem />
child component:
const Todo = ({ list }) => {
return (
<ul>
{list.map((item) => (
<TodoItem item={item} />
))}
</ul>
);
};
As for this new child component, it will simply be returning the <li>
item with list title
to display after mapping:
const TodoItem = ({ item }) => {
return <li>{item.title}</li>;
};
At this point, your code should look like the following:
import React from "react";
const App = () => {
const [todo, setTodo] = React.useState([
{ title: "Shop groceries 🛒" },
{ title: "Do yoga 🧘" }
]);
const [text, setText] = React.useState("");
const handleText = (e) => {
setText(e.target.value);
};
const handleAddTodo = () => {
setTodo(todo.concat({ title: text }));
};
return (
<div>
<input type="text" value={text} onChange={handleText} />
<button type="button" onClick={handleAddTodo}>
<span role="img" aria-label="add emojie">
➕
</span>
Add todo
</button>
<Todo list={todo} />
</div>
);
};
const Todo = ({ list }) => {
return (
<ul>
{list.map((item) => (
<TodoItem item={item} />
))}
</ul>
);
};
const TodoItem = ({ item }) => {
return <li>{item.title}</li>;
};
export default App;
The app should also look and work like this:
At this point in time, we successfully made a dead-simple to-do app using Hooks. Adding 3-4 items is fine. But what if our list was huge like hundreds or thousands of items? Maybe our app was using this list to render items fetched by an external database or API?
Then we need to take care of performance improvements so that our app doesn’t feel slow to use as it will be rendering multiple components and elements!
Step 4: Optimizing the components with React Memo
Let’s switch gears to memoizing some of the useful components. The first is the <Todo />
component. Wrap it around React.memo
like this:
// Memoized Todo component
const Todo = React.memo(({ list }) => {
console.log("Render: Todo");
return (
<ul>
{list.map((item) => (
<TodoItem item={item} />
))}
</ul>
);
});
With this, only the <App />
component will re-render because it's the only component that is affected by the change of state. The <Todo />
component’s state doesn’t change hence, it doesn’t do unnecessary renders improving app performance.
By default, when the to-do list changes it causes the <Todo />
component to update. Now let’s take it a step further by rendering only the newly added item to the list instead of all items at once by memoizing the <TodoItem />
child component as follows:
const TodoItem = React.memo(({ item }) => {
return <li>{item.title}</li>;
});
Now all the previous to-dos will remain the same and won’t re-render. What will actually re-render are all of the components affected by the state change.
Let’s see the memoization effect in action by introducing some log statements after the <App />
, <Todo />
and <TodoItem />
components as such:
// App component
const App = () => {
console.log("App component render");
.
.
.
// Todo component
const Todo = React.memo(({ list }) => {
console.log("Todo component render");
.
.
.
// TodoItem component
const TodoItem = React.memo(({ item }) => {
console.log("TodoItem component render");
.
.
.
By doing this we get to know when each of them renders while we add new to-do items. Now if we open our browser’s console pane we can see the following:
As we can see both the <Todo />
and <TodoItem />
components render only once which is why we used React Memo here!
Conclusion
React Memo is one of the most useful tools when it comes to optimizing the performance of your React components. If we use it correctly (and not over-use it), then it can impact the interaction of our app to a great extent. The effect is mostly seen on larger applications that consume more data.
Of course, we can’t use it everywhere, and also React doesn’t apply it to its default starter project because under the hood the React.memo()
function has to compare the previous props with the new props to decide whether to re-render or not.
From here, you can read the following topics to better understand React Memo:
Follow us@ordinarycoders