Join us live as we unveil the all new Hygraph Studio!

What is React memo and how to use it?

In this article, you will learn what React Memo means, what it does, how it works, and when or when not to use it. You will also learn how it works with detailed examples and codes.
Joel Olawanle

Joel Olawanle

Nov 15, 2022
What is React Memo and How to Use it

One of the benefits of using React is it’s improved performance, which allows your web applications to load faster and allows you to navigate from one page to another without having to wait so long. This built-in performance optimization has some drawbacks that you can work on to improve the performance of your web applications.

One of the most common and reliable methods for optimizing the performance of your React application is to "memoize" the code you write in your React components using React Memo. This allows you to avoid unnecessary re-renders, enhancing the performance of your React application.

What is Memoization?

Memoization is a form of caching used to store results of expensive functions and avoid repeated calls, leading to repetitive computation of results.

In this article, you will learn what React Memo means, what it does, how it works, and when or when not to use it. You will also learn how it works with detailed examples and codes.

#What is React Memo?

Components in React are designed to re-render whenever the state or props value changes. However, this can impact your application's performance because, even if the change is only intended to affect the parent component, every other child component attached to the parent component will re-render. When a parent component re-renders, so do all of its child components.

React Memo is a higher-order component that wraps around a component to memoize the rendered output and avoid unnecessary renderings. This improves performance because it memoizes the result and skips rendering to reuse the last rendered result.

There are two ways you can wrap your component with React.memo(). It is either you wrap the actual component directly without having to create a new variable to store the memoized component:

const myComponent = React.memo((props) => {
/* render using props */
});
export default myComponent;

Another option is to create a new variable to store the memoized component and then export the new variable:

const myComponent = (props) => {
/* render using props */
};
export const MemoizedComponent = React.memo(myComponent);

In the example above, myComponent outputs the same content as MemoizedComponent, but the difference between both is that MemoizedComponent render is memoized. This means that this component will only re-render when the props change.

#When to use React Memo

You now know what it means to memoize a component and the advantages of optimization. But this doesn’t mean you should memoize all your components to ensure maximum performance optimization of performance 🙃.

It is important to know when and where to memoize your component else it would not fulfill its purpose. For example, React Memo is used to avoid unnecessary re-renders when there is no change to the state or context of your component. If the state and content of your component will ALWAYS change, React Memo becomes useless. Here are other points:

  • Use React Memo if your component will render quite often.
  • Use it when your component often renders with the same props. This happens to child components who are forced to re-render with the same props whenever the parent component renders.
  • Use it in pure functional components alone. If you are using a class component, use the React.PureComponent.
  • Use it if your component is big enough (contains a decent amount of UI elements) to have props equality check.

#How to use React Memo

At this point, you now understand when to use React Memo and what it does. The major point is that you should use it when your component often renders with the same props.

Suppose you have a React application such as a Todo application in which you decide to break up the application into separate components to ensure it is easy to understand and use. You will have the parent component that holds your todo array in a state. The todo array will be passed as a prop to the Todo component:

<!-- App.js -->
import React, { useState } from 'react';
import Todo from './Todo';
const App = () => {
console.log('App component rendered');
const [todo, setTodo] = useState([
{ id: 1, title: 'Read Book' },
{ id: 2, title: 'Fix Bug' },
]);
const [text, setText] = useState('');
const addTodo = () => {
let newTodo = { id: 3, title: text };
setTodo([...todo, newTodo]);
};
return (
<div>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button type="button" onClick={addTodo}>
Add todo
</button>
<Todo list={todo} />
</div>
);
};
export default App;

In the component above, there is a state to hold all the todo in an array, and then there is a form you would use to add a new todo to the array. Notice that at the beginning of the component, a console.log() statement will be implemented once your application renders.

The todo items are to be passed as props to the Todo component, which has been imported. This means that the Todo component is a child component of the App component. In the Todo component, the todo array that has been passed as a prop named list will be iterated so that you can access each item in the array and display:

<!-- Todo.js -->
import React from 'react';
import TodoItem from './TodoItem';
const Todo = ({ list }) => {
console.log('Todo component rendered');
return (
<ul>
{list.map((item) => (
<TodoItem key={item.id} item={item} />
))}
</ul>
);
};
export default Todo;

In the code above, a console.log() statement will display a text to show when the Todo component renders or re-renders. In the component above, each todo item is passed as a prop to a TodoItem component. This is done to properly explain what happens and when you need to memoize a component:

<!--TodoItem.js -->
const TodoItem = ({ item }) => {
console.log('TodoItem component rendered');
return <li>{item.title}</li>;
};
export default TodoItem;

The component above also has a console.log statement to help you know when it renders and re-renders. When you run your application and check the console, you will notice that all three components render.

checking rendered components in the console

Our component renders four times, once for the parent component (App.js), once for the Todo component, and finally, the TodoItem component renders twice because it has two elements. This means altogether, the application renders four times. This is normal for the initial render. But when you change something in the parent component that doesn’t affect the child components, only the parent component should be rendered.

For example, when you type anything in the text field, the App component and the Todo component will always render even though the input does not affect the Todo component. Also, if you have more todo items, the application will render more times, leading to poor performance.

Optimizing components with React Memo

Let’s now memoize some of these components so that they only render when there is a change in props. The first component that would be memoized is the Todo component. It is important to stop re-renders whenever a user types in the text field because it affects the state.

You can do this by wrapping the Todo component with React.memo():

<!-- Todo.js -->
// Memoized Todo component
import React from 'react';
import TodoItem from './TodoItem';
const Todo = React.memo(({ list }) => {
console.log('Todo component rendered');
return (
<ul>
{list.map((item) => (
<TodoItem key={item.id} item={item} />
))}
</ul>
);
});
export default Todo;

Now that the Todo component is memoized, only the <App /> component will re-render whenever the state changes because it's the only component affected. The <Todo /> component will only re-render when the todo state changes, affecting the list prop.

Let’s now take a further step to avoid unnecessary re-rendering whenever an item is added to the todo array. When the todo state changes affecting the list prop, the TodoItem component will render for each item added. This can become bad when you have so many items.

You would want a situation where once an item is rendered, it won’t re-render; instead, only the new item added will cause the component to render. You can achieve this by memoizing the TodoItem component:

<!--TodoItem.js -->
// Memoized TodoItem component
import React from 'react';
const TodoItem = React.memo(({ item }) => {
console.log('TodoItem component rendered');
return <li>{item.title}</li>;
});
export default TodoItem;

Now when you load your application, all the components will render, then the components will now only render when there is a change in the prop. This is done by making a shallow comparison to know if the value changes.

How to use custom comparison function with React Memo

React Memo makes a shallow comparison and might not function as you wish in some scenarios. If you want control over the comparison, you can provide a custom comparison function as the second argument.

For example, if you are passing an object containing user details as a prop to a Profile component:

<!-- App.js -->
import { useState } from 'react';
import Profile from './Profile';
const App = () => {
console.log('App rendered');
const [text, setText] = useState('');
let user = { name: 'John Doe', age: 23, userName: 'Jonny' };
return (
<div>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<Profile userDetails={user} />
</div>
);
};
export default App;

The memoized Profile component will always render even when the user object does not change:

<!-- Profile.js -->
import React from 'react';
const Profile = React.memo(({ userDetails }) => {
console.log('profile rendered');
return (
<div>
<p>{userDetails.name}</p>
<p>{userDetails.age}</p>
<p>{userDetails.userName}</p>
</div>
);
});
export default Profile;

React Memo doesn't work because it only performs a shallow comparison of the component's properties. Every time the app is updated, the user variable is re-declared, which happens when you use objects. To fix this, use the second argument and provide a custom comparison function.

<!-- Profile.js -->
import React from 'react';
const Profile = React.memo(({ userDetails }) => {
console.log('profile rendered');
return (
<div>
<p>{userDetails.name}</p>
<p>{userDetails.age}</p>
<p>{userDetails.userName}</p>
</div>
);
},(prevProps, nextProps) => {
if (prevProps.userDetails.name === nextProps.userDetails.name) {
return true; // props are equal
}
return false; // props are not equal -> update the component
}
);
export default Profile;

#When to avoid using React Memo

You now understand how to use React Memo, but it is important to note that you should not use it in all situations because it does not always work as expected. Performance-related changes that are implemented incorrectly can even harm performance.

When you need to remember the values of a function or an object, you can use hooks like useMemo() and useCallback(). Also, avoid using React Memo if the component is light and renders with multiple props.

Finally, never use React Memo to wrap a class-based component; instead, extend PureComponent or implement the shouldComponentUpdate() method.

#Wrapping Up

You've learned how, why, and when to use React Memo in this article. You've also learned that using React Memo correctly prevents unnecessary re-renderings when the next props are equal to the previous ones.

Have fun coding!

Blog Author

Joel Olawanle

Joel Olawanle

Joel Olawanle is a Frontend Engineer and Technical writer based in Nigeria who is interested in making the web accessible to everyone by always looking for ways to give back to the tech community. He has a love for community building and open source.

Share with others

Sign up for our newsletter!

Be the first to know about releases and industry news and insights.