Avoid List Re-Render in React - Best Practices
Prevent List Re-Render in React - Best Practices for Optimizing React Performance. How can I avoid unnecessary re-rendering in a React component, especially in a list component? Poor performance and a bad user experience can result from excessive re-renders. Many developers struggle to find the best practices to address this issue.
In this article, I will provide a step-by-step guide to understanding why your list component may be re-rendering unnecessarily, and I will offer solutions to address it. I will also explore additional best practices to optimize your React components.
Before we dive in, I encourage you to leave a comment with any questions or feedback you may have. Your input helps me improve my content and make it more helpful to all readers.
Now let's take a look at this example :
// ListItem.tsx
import { FC, SyntheticEvent } from "react";
interface ListItemProps {
item: number;
onClick: (item: number) => void;
}
const ListItem: FC<ListItemProps> = ({ item, onClick }) => {
const handleClick = (e: SyntheticEvent) => {
e.preventDefault();
onClick(item);
};
const random = item * Math.floor(Math.random() * 10);
return (
<button onClick={handleClick} className="border cursor p5">
<p>{item}</p>
<p>Random: {random}</p>
</button>
);
};
export default ListItem;
Here we have an ListItem
component with two props: item
and onClick
. I added a random
number to every render in this component, which makes it easier to understand the behavior of the component and what is going on.
// List.tsx
import { useCallback, useState } from "react";
import ListItem from "./ListItem";
const items = Array.from(Array(1000)).map((_, i) => i);
export const List = () => {
const [selected, setSelected] = useState<any>(0);
const handleClick = useCallback((item: number) => {
setSelected(item);
}, []);
return (
<div className="p5">
<h1 className="title">{selected}</h1>
<div>
{items.map((item) => (
<ListItem item={item} key={item} onClick={handleClick} />
))}
</div>
</div>
);
};
In this component, we define a state called 'selected
' where we store the currently selected item
, okay?
Let me ask you a question: Is there something wrong with these two components? And if so, what is the issue?
The short answer is NO! This code works, but...yes, this code has very problematic best practices for performance optimization in React.
Let's take a look at the result :
Now, you can see what is wrong here. Every time you click on an item
, the selected
state changes and causes the entire list to re-render.
Let's find a solutions to address this issue.
How To Prevent List Re-Render in React
Yes! You can avoid list re-renders by using built-in React functionalities and some best practices. Here's how to address this issue:
Check if the Parent Component Re-Render
In React, when one state changes, the component re-renders. Before we jump into the solutions, it's important to determine whether the parent component is causing the re-render. You can use React Developer Tools to inspect the components and see which components are re-rendering.
Check All Props Being Passed from Parent Component
After checking if the parent component is re-rendering, the next step is to ensure that all the props passed from the parent component to the child component are necessary. Sometimes, a parent component may pass unnecessary props to its child components, which can cause unnecessary re-renders.
Use React.memo to Avoid Re-Render
React.memo
is a higher-order component (HOC) that you can use to avoid re-rendering a component if none of its props have changed. By using React.memo
, you can optimize the performance of your React app by preventing unnecessary re-renders.
What is React.memo? How does it work? We'll explain it in the next section.
Use Memoization
Memoization is a technique used to memoize functions or values, which can help optimize the performance of your React app.
useCallback hook
useCallback
is a React Hook that memoizes a function, preventing it from being recreated on each render. By using useCallback, you can optimize the performance of your React app by preventing unnecessary function recreations.
useMemo hook
useMemo
is another React Hook that memoizes a value, preventing it from being recomputed on each render. By using useMemo, you can optimize the performance of your React app by preventing unnecessary computations.
By using these techniques, you can optimize the performance of your React app and avoid unnecessary re-renders.
How To use React.memo To Avoid re-render
React.memo
is a higher-order component (HOC) provided by React that can be used to prevent unnecessary re-renders of functional components. It's used to wrap a component and will only re-render the component if the props passed to it have changed.
memo
lets you skip re-rendering a component when its props are unchanged - React Documentation
const MemoizedComponent = memo(SomeComponent, arePropsEqual?)
Let's wrap our ListItem
component with React.memo.
import { FC, memo, SyntheticEvent } from "react";
const ListItem: FC<ListItemProps> = ({ item, onClick }) => {
// .....
};
export default memo(ListItem);
NOT GOOD !
But wait! The entire list is still re-rendering. What's going on?
Even though we wrapped the ListItem component with React.memo
, the entire list still re-renders. This is because the props we're passing from the List component change every time the parent component re-renders. You might be wondering which props are causing the issue: is it item
or onClick
?
Actually, the item
prop never changes. The problem is with onClick
.
In the List
component, the handleClick
function is causing the issue. Every time the List component re-renders, a new instance of the function is created and its reference changes. This can cause the ListItem
components to re-render even if their props haven't changed. To solve this problem, we can use memoization techniques such as useCallback and useMemo.
How To use useCallback to Stop re-render in List Component?
The useCallback
hook returns a memoized version of a callback function that only changes when one of its dependencies has changed.
In this case, we can use useCallback to memoize the handleClick function in the List component. The memoized handleClick
function will only be re-created if any of its dependencies change, which means that it won't cause unnecessary re-renders of the ListItem
components.
Here's how we can use useCallback in the List component:
const handleClick = useCallback((item: number) => {
setSelected(item);
}, []);
Now let's see the result
That's it ! Now your List Component is optimized and stop to re-render.
what is the difference between useCallback and useMemo ?
useCallback
and useMemo
are two hooks in React that can help optimize the performance of functional components.
The main difference between useCallback and useMemo is that useCallback is used to memoize functions, whereas useMemo is used to memoize values.
useCallback is used to memoize functions so that they do not need to be redefined on every render. It takes a function as its first argument and an array of dependencies as its second argument. The function will only be recreated if one of the dependencies in the array changes. This can be useful when passing a function down as a prop to a child component, as it ensures that the child component only re-renders if the function reference changes.
useMemo is used to memoize values so that they do not need to be recomputed on every render. It takes a function as its first argument and an array of dependencies as its second argument. The function will only be recomputed if one of the dependencies in the array changes. This can be useful when calculating expensive values, as it ensures that the value is only recomputed when necessary.
You can also check my tutorials:
UseDebounce(): Optimizing React Performance - Complete Guide
Advantages of Using TypeScript in Large-Scale React Development (2023)
How To Send A Cookie In getServerSideProps Next.JS - Complete Guide
In conclusion, list re-rendering can be a common issue when working with React. However, it can be avoided by implementing some best practices and using built-in React functionalities such as React.memo
, useCallback
and useMemo
. By analyzing the props being passed from parent to child components and using memoization, we can significantly improve the performance of our React applications. It is important to keep in mind that optimizing performance is a continuous process and should be integrated into the development workflow from the beginning.
I'm always looking to improve my content - if you have any feedback or suggestions for future articles, I'd love to hear from you in the comments.
By Mahady Manana, Fullstack Javascript Developer
23 February 2023 at 10 h 02
"Skills are honed through the interplay of learning and teaching - one must learn in order to teach, and teach in order to truly learn”. As a passionate Javascript developer, I enjoy sharing my knowledge and skills with others, and regularly publish tutorials on my website and other platforms about the latest Javascript, Typescript, Node.js, React.js, Next.js and more to help others learn and stay up to date with the latest developments trends.