6
minutes
Mis à jour le
26/9/2024


Share this post

Comment optimiser les re-renders dans React pour améliorer les performances de votre application. Cet article explique les causes des re-renders, leurs impacts, et présente des stratégies efficaces comme React.memo, le découpage du code et la gestion d'état pour réduire les re-renders inutiles

#
React
Hugo Poujol
Software Engineer

1. Introduction

React is a library used to simply build applications using components, these components are then re-rendered based on state and props change. However, improper usage of state and props change can lead to unnecessary re-renders, which can impact the application's performance and therefore the user experience. The purpose of this article is to explain how re-renders work in React and to present some practical strategies to optimize these re-renders.

1.1 TL;DR

React re-renders components when state, props, or context changes, but unnecessary re-renders can harm performance, leading to issues like laggy UIs and increased memory usage. Detecting re-renders using tools like React DevTools or the Why Did You Render library is crucial for optimizing your app. Key strategies to reduce unnecessary re-renders include:

  • React.memo: Memoizes functional components to prevent re-rendering unless props change. However, use it sparingly as it adds overhead.
  • Code Splitting: Break down components into smaller parts to ensure only necessary components re-render.
  • State Management (Stores): Use global state stores (like Zustand) to allow components to subscribe to only the specific state slices they need.

Optimizing re-renders ensures a smoother UI, but the balance between the performance of the application and the maintainability of the code should always be considered.

1.2 What Causes a Component to Re-render?

There are several reasons that can lead to a component’s re-render:

  • Parent component re-render: If the parent component re-renders, all its children will by default be re-rendered, unless they are optimized.
  • State updates: If one of the component’s state changes, React triggers a re-render.
  • Props changes:  If a parent component passes down props to a child component, this child component will then be re-rendered if one of the props is updated.
  • Context updates: If a component is subscribed to a context, any change in the context will trigger a re-render.

1.3 What Is the Impact on Performance?

Too many re-renders can increase the amount of computation React has to perform, which leads to performance issues. It can be especially noticeable in large apps or when the re-rendered components trigger expensive operations (e.g., API calls, complex calculations).

In an application, this can lead to:

  • Laggy UI: Visible delays when interacting with the UI.
  • Increased memory usage: Repeated re-creation of components can lead to high memory consumption.
  • Lower FPS: Frames per second drop, causing a poor user experience.

1.4 How to Detect Re-renders

Detecting re-renders is key in order to optimize your application’s performances. Here are some methods that can help detect and understand re-renders:

  • React DevTools: The React Developer Tools extension contains the feature "Highlight Updates" which visually indicates which components re-render by adding a colored border every time it is re-rendered.
  • Why Did You Render (WDYR): Once integrated into your application, the library WDYR will notify you about potentially avoidable re-renders in the console logs by indicating which components were re-rendered and what caused the re-render.

2. Strategies to Optimize Re-rendering

2.1 React.memo

React.memo allows you to memoize a component which can help preventing unecessary re-renders. If a component is wrapped in React.memo , it will only be re-rendered if one of its props changes.

This solution is suitable for functional components with heavy computation or when you know the props won't change frequently.

However, inappropriate usages of it might cause performance issues as React needs to compare the props everytime the parent component is re-rendered. It can be the case if for example a memoized component with props is wrapped in a parent component which re-render at any input of the user.

Before

In this example, every time the user sends a message, the components Header , MessageList and MessageInput are re-rendered.

After

If we decide to memoize the header with React.memo, the header is then rendered on  the page load and will never be re-rendered as none of its props change.

2.2 Code Splitting

Splitting code not only keeps it readable and maintainable, but also helps optimize re-renders. In fact, splitting a component into sub-components allows state changes to be placed at a lower level, allowing only the components that needs to be re-rendered to be re-rendered.

Before

In this example, every time the user enters a character in the text input the state textInput changes leading to the re-render of all the components inside Component

After

If we decide to use a zustand store to store the messages and subscribe to the list of messages in the component MessageList. Then every time the user sends a message, only the component MessageList will be re-rendered.

2.3 Use Stores for State Management

Global state management using stores (like Zustand) can minimize unnecessary re-renders, especially when managing global state across multiple components. With the stores, it allows your components to subscribe a specific slice of state, reducing unnecessary re-renders in component that don’t use the full store.

Before

In this example, every time the user clicks on Send the list of messages is updated, leading to the re-render of the Header, the MessageList and the MessageInput.

After

If we decide to use a zustand store to store the messages and subscribe to a list of messages in the component MessageList. Then every time the user sends a message, only the component MessageList will be re-rendered.

3. Conclusion

Optimized re-renders are essential in order to guarantee a seamless and effective user experience, particularly when the component tree's complexity increases. This can be achieved by using strategies like React.memo, code splitting and the stores which will help you minimize unnecessary renders.
Developers should monitor re-renders closely, however the balance between the performance of the application and the maintainability of the code should always be considered.