This page looks best with JavaScript enabled

React Performance: How to avoid redundant re-renders

 ·  ☕ 7 min read  ·  ✍️ Iskander Samatov

React Performance- How to avoid redundant re-renders


Redundant re-renders are a common issue in React. If not taken seriously, this issue can quickly worsen the performance of your application.

By understanding and implementing these practices, you can avoid the problem and keep your rendering process running smoothly.

memo and useCallback

Let’s first cover the main toolkit under your belt for cutting down the re-renders: memo and useCallback. You should know how these utility functions work in order to be able to optimize React performance.

memo is a utility method that React library provides to memoize the state of the components and make it so that they only render when their props change. All you need to do is wrap your components declaration with the function:

react avoid re-renders memo example

Now, the ListItem component will no longer re-render when its parent does. The only time the ListItem would re-render is if the props passed to it change. This is especially useful when working with large lists. List items will not re-render each time the state of the parent list changes. This tweak could drastically improve the performance of your UI.

So what about useCallback ? In the previous example, our ListItem did not receive any event handler props. Yet passing event handlers from parent to child is a common pattern that you will, no doubt, use a lot when working with React.

There is, however, a slight problem with passing event handlers to memoized components. Event handlers are usually declared inside of the parent component, which means they will re-render each time the parent component does. This will make wrapping our child components with memo useless since the event handler prop will cause it re-render each time anyway.

And that’s where useCallback comes in. This utility method returns a memoized version of any callback function it receives. The only time it would recalculate the value of the function you pass is if any of the dependencies you provided change.

Let’s see how we can use useCallback to pass an event handler to our ListItem without causing redundant re-renders:

react avoid re-renders useCallback example

In the example above, we wrapped the handleClick event handler with useCallback. useCallback will memoize our handler function after the initial render. Next time our parent list re-renders, useCallback will return the same memoized version of our handler function. The ListItem component will thus avoid a redundant re-rendering since none of its props changed.

Note: Whenever you use memo to reduce re-renders for a component, be sure to also wrap the event handler properties that the component receives with useCallback. Otherwise, your efforts are futile.

Be discriminating about what your components get to decide

Whenever you create a React component, keep in mind the scope of the context you’re giving it. Ask yourself if the component should really be making decisions about certain things.

For example, it’s rare for components to need to decide if they should render themselves. It’s usually better for the parent components to make that decision.

When rendering lists, err on the side of providing strictly the props the list items need to function. Try to avoid passing any of the state variables of the parent to the child. Passing parent’s state variables will cause ALL of the list items to re-render each time that variable changes. The negative effect of this is amplified as the size of your list grows.

Instead, try to derive a primitive prop based on the updated state of the parent. This way, the child components only need to re-render when the primitive changes.

Let’s take a look at an example:

react avoid re-renders component decisions wrong

Here we have the ListItem for displaying items inside the List. ListItem accepts id, selectedId and title properties. We’re also outputting the “Rendered” word to the console each time the ListItem renders to see how many times it rendered. Note that we’re also wrapping our ListItem with memo to reduce the number of re-renders for each item.

For the sake of the example, we’re setting selectedItemId to 2 inside the useEffect. Doing so will trigger another re-render of the parent component after the initial render.

After running the example above, the console will output the word “Render” a total of 6 times: 3 times during the initial render of each list item and 3 more times when the setSelectedItemId is called.

Now let’s see how we can avoid the redundant renders:

react avoid re-renders component decisions correct

In this example, ListItem receives the isSelected prop that lets it know whether the item is selected. Now it’s up to the parent List component to make that decision. As a result, the word “Rendered” will only be printed 4 times. That’s because the items that weren’t selected didn’t re-render since none of their props changed.

While cutting down from 6 to 4 renders might not seem like a big deal, when you’re working with large lists that render a lot of data and have sizeable component trees, you can significantly improve their performance using this approach.

Use flattened props and primitives

This tip relates to the previous one. When passing complex data structures as props, try to pass flattened and simplified versions instead of the whole objects.

Better yet, try to extract the fields your component needs from the data structure as primitive props. This will make it easier to optimize the performance of your components in the future.

Let me show you what I mean. Say we have a Profile component that displays user information:

react avoid re-renders flat props wrong

Nothing particularly terrible about this component. But, we should ask ourselves: “Do we really need the whole pass the whole user object?”

Since we’re only using two fields from the object, we could accept them as primitive props instead:

react avoid re-renders flat props correct

If the user object got re-created, which can easily happen multiple times within the component’s lifetime, this will not cause re-renders for our Profile component. Profile doesn’t care if the user object gets recreated, as long as the values of the title and name fields stay the same. Now it’s much easier to optimize Profile by wrapping it with memo.

Be cautious with useEffect

useEffect is how you tap into the lifecycle of the component. However, I often see it being used in places it could’ve been avoided. The main issue I see with overusing useEffect is that your app will be full of unintended side effects that might cause redundant re-renders or, worse, redundant server calls.

Let’s look at an example. Say we have a UserList component that displays user profiles. You can expand each profile to view more info:

react avoid re-renders component useEffect wrong

In the code above, we’re using useEffect to track when we should be fetching additional info but is that necessary? A simpler and more future-proof solution is to fetch additional information inside of the expand click handler:
react avoid re-renders component useEffect correct

The main reason this is better is that as the complexity of your component grows, you might introduce other pieces of logic that will change selectedUserId but that don’t necessarily need to request additional info. If you’re not careful, you might trigger requests in cases where you didn’t intend to.

Often using useEffect is justified and might even be the only viable solution. With that said, I suggest double checking if you need useEffect. Sometimes you can get away with relying on other, safer triggers, such as user actions.

Conclusion

In this post, we looked at some of the practices for optimizing your React rendering process. By following these tips, you can avoid unnecessary re-renderings and improve the performance of your app. I hope you find them useful!

If you would like more tips on writing better React code, check out my other post “ Simple tips for writing clean React components ”.

If you’d like to get more web development, React and TypeScript tips consider following me on Twitter, where I share things as I learn them.
Happy coding!

Share on

Software Development Tutorials
WRITTEN BY
Iskander Samatov
The best up-to-date tutorials on React, JavaScript and web development.