React.memo helps you to avoid re-rendering of computationally heavy components
React.memo
is useful when your component have some heavy computation or some expensive operations. It is similar to PureComponent but it works with functional components.
Let's say we have a component that renders a list of items. The list of items is passed as a prop to the component. The component renders the list of items as a list of <li>
elements.
// BookList.js
const BookList = ({ items }) => (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)
export default List
Now, let's say we have a parent component that renders the BookList
component. The parent component has a state that contains the list of items. The parent component renders the BookList
component with the list of items from the state.
// App.js
const App = () => {
const [items, setItems] = useState([])
useEffect(() => {
fetch('/api/books')
.then((res) => res.json())
.then((data) => setItems(data))
}, [])
return <BookList items={items} />
}
export default App
Let's render the BookItem
component for each item in the list. The BookItem
component renders the details of the book. The BookItem
component fetches the details of the book from the server when the component is mounted. The BookItem
component renders the details of the book when the details are available.
// BookList.js
const BookList = ({ items }) => (
<ul>
{items.map((item) => (
<BookItem key={item.id} item={item} />
))}
</ul>
)
// BookItem.js
const BookItem = ({ item }) => {
const [details, setDetails] = useState(null)
useEffect(() => {
fetch(`/api/books/${item.id}`)
.then((res) => res.json())
.then((data) => setDetails(data))
}, [item.id])
// Compute the average rating of the book
// TODO: This can be optimized with useMemo, I will cover that in next article separately
const averageRating = details
? details.reviews.reduce((acc, review) => acc + review.rating, 0) /
details.reviews.length
: null
return (
<li>
<h3>{item.name}</h3>
<p>{item.author}</p>
<p>Average Rating: {averageRating}</p>
</li>
)
}
Fetching the details of the book is an expensive operation. We don't want to fetch the details of the book when the list of items is changed. We only want to fetch the details of the book when the component is mounted. We can use React.memo
to avoid re-rendering of the BookItem
component when the list of items is changed.
// BookList.js
const BookList = ({ items }) => (
<ul>
{items.map((item) => (
<BookItem key={item.id} item={item} />
))}
</ul>
)
// BookItem.js
const BookItem = ({ item }) => {
const [details, setDetails] = useState(null)
useEffect(() => {
fetch(`/api/books/${item.id}`)
.then((res) => res.json())
.then((data) => setDetails(data))
}, [item.id])
// Compute the average rating of the book
const averageRating = details
? details.reviews.reduce((acc, review) => acc + review.rating, 0) /
details.reviews.length
: null
return (
<li>
<h3>{item.name}</h3>
<p>{item.author}</p>
<p>Average Rating: {averageRating}</p>
</li>
)
}
// Memoize the component to avoid re-rendering when the list of items is changed
export default React.memo(BookItem)
What if only few items in the list is changed?
It will re-render only the items that are changed. It will not re-render the items that are not changed.
When to use React.memo
- Real time apps that updates frequently. Example: Sports tracker app that renders the details of a match live. The details of the match are fetched from the server. The details of the match are updated every few seconds. You can memoize the component to avoid re-rendering of the component when the details of the match are updated. It helps to consume less CPU and memory.
- Apps that renders a list of items and updates it more frequently. Example: A chat app that renders the list of messages. The list of messages are updated frequently. You can memoize the component to avoid re-rendering of the component when the list of messages are updated.
When not to use React.memo
- When the component is not expensive to render. Example: A component that renders a button. It is not expensive to render a button. You don't need to memoize the component. Pre-mature optimization is always evil š¤£
- When the component is expensive to render but the component is not re-rendered frequently. Example: A component that renders a list of items. The list of items are fetched from the server. The list of items are not updated frequently. You don't need to memoize the component.
Hope you learned something new today š¤. Keep learning and share your comments by tagging me on twitter @learnwithparam š