Les tableaux et les listes très volumineux peuvent ralentir considérablement les performances de votre site. La virtualisation peut vous aider.
react-window
est une bibliothèque qui permet d'afficher efficacement de longues listes.
Voici un exemple de liste contenant 1 000 lignes rendues avec react-window
. Faites défiler la page aussi vite que possible.
En quoi est-ce utile ?
Il peut arriver que vous deviez afficher un grand tableau ou une longue liste contenant de nombreuses lignes. Le chargement de chaque élément d'une telle liste peut avoir un impact considérable sur les performances.
La virtualisation de liste, ou "fenêtrage", est le concept qui consiste à n'afficher que ce qui est visible par l'utilisateur. Le nombre d'éléments affichés au début est un très petit sous-ensemble de la liste entière, et la "fenêtre" de contenu visible se déplace lorsque l'utilisateur continue de faire défiler la page. Cela améliore les performances de rendu et de défilement de la liste.

Les nœuds DOM qui quittent la "fenêtre" sont recyclés ou immédiatement remplacés par des éléments plus récents lorsque l'utilisateur fait défiler la liste vers le bas. Cela permet de conserver le nombre de tous les éléments affichés en fonction de la taille de la fenêtre.
react-window
react-window
est une petite bibliothèque tierce qui facilite la création de listes virtualisées dans votre application. Il fournit un certain nombre d'API de base qui peuvent être utilisées pour différents types de listes et de tableaux.
Quand utiliser des listes de taille fixe
Utilisez le composant FixedSizeList
si vous avez une longue liste unidimensionnelle d'éléments de taille égale.
import React from 'react';
import { FixedSizeList } from 'react-window';
const items = [...] // some list of items
const Row = ({ index, style }) => (
<div style={style}>
{/* define the row component using items[index] */}
</div>
);
const ListComponent = () => (
<FixedSizeList
height={500}
width={500}
itemSize={120}
itemCount={items.length}
>
{Row}
</FixedSizeList>
);
export default ListComponent;
- Le composant
FixedSizeList
accepte une propriétéheight
,width
etitemSize
pour contrôler la taille des éléments de la liste. - Une fonction qui affiche les lignes est transmise en tant qu'enfant à
FixedSizeList
. Vous pouvez accéder aux détails d'un élément spécifique à l'aide de l'argumentindex
(items[index]
). - Un paramètre
style
est également transmis à la méthode de rendu des lignes et doit être associé à l'élément de ligne. Les éléments de liste sont positionnés de manière absolue, avec leurs valeurs de hauteur et de largeur attribuées en ligne. Le paramètrestyle
est responsable de cela.
L'exemple Glitch présenté plus haut dans cet article montre un exemple de composant FixedSizeList
.
Quand utiliser des listes de taille variable ?
Utilisez le composant VariableSizeList
pour afficher une liste d'éléments de différentes tailles. Ce composant fonctionne de la même manière qu'une liste de taille fixe, mais attend une fonction pour la propriété itemSize
au lieu d'une valeur spécifique.
import React from 'react';
import { VariableSizeList } from 'react-window';
const items = [...] // some list of items
const Row = ({ index, style }) => (
<div style={style}>
{/* define the row component using items[index] */}
</div>
);
const getItemSize = index => {
// return a size for items[index]
}
const ListComponent = () => (
<VariableSizeList
height={500}
width={500}
itemCount={items.length}
itemSize={getItemSize}
>
{Row}
</VariableSizeList>
);
export default ListComponent;
L'intégration suivante montre un exemple de ce composant.
La fonction de taille d'élément transmise à la propriété itemSize
randomise les hauteurs de ligne dans cet exemple. Toutefois, dans une application réelle, une logique réelle devrait définir la taille de chaque élément. Idéalement, ces tailles doivent être calculées à partir de données ou obtenues à partir d'une API.
Grilles
react-window
est également compatible avec la virtualisation des listes ou des grilles multidimensionnelles. Dans ce contexte, la "fenêtre" de contenu visible change lorsque l'utilisateur fait défiler la page horizontalement et verticalement.

De même, les composants FixedSizeGrid
et VariableSizeGrid
peuvent être utilisés selon que la taille des éléments de liste spécifiques peut varier ou non.
- Pour
FixedSizeGrid
, l'API est à peu près la même, mais les hauteurs, les largeurs et le nombre d'éléments doivent être représentés pour les colonnes et les lignes. - Pour
VariableSizeGrid
, vous pouvez modifier la largeur des colonnes et la hauteur des lignes en transmettant des fonctions au lieu de valeurs à leurs propriétés respectives.
Consultez la documentation pour voir des exemples de grilles virtualisées.
Chargement différé au défilement
De nombreux sites Web améliorent leurs performances en attendant que l'utilisateur ait fait défiler la page vers le bas pour charger et afficher les éléments d'une longue liste. Cette technique, communément appelée "chargement infini", ajoute de nouveaux nœuds DOM à la liste à mesure que l'utilisateur dépasse un certain seuil proche de la fin. Bien que cette méthode soit préférable au chargement de tous les éléments d'une liste à la fois, elle finit tout de même par remplir le DOM avec des milliers d'entrées de ligne si l'utilisateur a fait défiler la page au-delà de ce nombre. Cela peut entraîner une taille de DOM excessivement grande, ce qui commence à avoir un impact sur les performances en ralentissant les calculs de style et les mutations du DOM.
Le schéma suivant peut vous aider à comprendre ce processus :

La meilleure approche pour résoudre ce problème consiste à continuer à utiliser une bibliothèque telle que react-window
pour maintenir une petite "fenêtre" d'éléments sur une page, mais aussi à charger de manière différée les nouvelles entrées à mesure que l'utilisateur fait défiler la page vers le bas. Un package distinct, react-window-infinite-loader
, permet d'utiliser react-window
.
Prenons l'exemple de code suivant, qui montre un exemple d'état géré dans un composant App
parent.
import React, { Component } from 'react';
import ListComponent from './ListComponent';
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: [], // instantiate initial list here
moreItemsLoading: false,
hasNextPage: true
};
this.loadMore = this.loadMore.bind(this);
}
loadMore() {
// method to fetch newer entries for the list
}
render() {
const { items, moreItemsLoading, hasNextPage } = this.state;
return (
<ListComponent
items={items}
moreItemsLoading={moreItemsLoading}
loadMore={this.loadMore}
hasNextPage={hasNextPage}
/>
);
}
}
export default App;
Une méthode loadMore
est transmise à un ListComponent
enfant qui contient la liste du chargeur infini. C'est important, car le chargeur infini doit déclencher un rappel pour charger plus d'éléments une fois que l'utilisateur a fait défiler la page au-delà d'un certain point.
Voici à quoi peut ressembler le ListComponent
qui affiche la liste :
import React from 'react';
import { FixedSizeList } from 'react-window';
import InfiniteLoader from "react-window-infinite-loader";
const ListComponent = ({ items, moreItemsLoading, loadMore, hasNextPage }) => {
const Row = ({ index, style }) => (
{/* define the row component using items[index] */}
);
const itemCount = hasNextPage ? items.length + 1 : items.length;
return (
<InfiniteLoader
isItemLoaded={index => index < items.length}
itemCount={itemCount}
loadMoreItems={loadMore}
>
{({ onItemsRendered, ref }) => (
<FixedSizeList
height={500}
width={500}
itemCount={itemCount}
itemSize={120}
onItemsRendered={onItemsRendered}
ref={ref}
>
{Row}
</FixedSizeList>
)}
</InfiniteLoader>
)
};
export default ListComponent;
Ici, le composant FixedSizeList
est encapsulé dans InfiniteLoader
.
Les props attribués au chargeur sont les suivants :
isItemLoaded
: méthode qui vérifie si un certain élément a été chargé.itemCount
: nombre d'éléments dans la liste (ou attendus)loadMoreItems
: rappel qui renvoie une promesse résolue en données supplémentaires pour la liste.
Une propriété de rendu est utilisée pour renvoyer une fonction que le composant de liste utilise pour le rendu.
Les attributs onItemsRendered
et ref
doivent être transmis.
Voici un exemple de fonctionnement du chargement infini avec une liste virtualisée.
Le défilement vers le bas de la liste peut sembler identique, mais une requête est désormais envoyée à une API d'utilisateur aléatoire chaque fois que vous approchez de la fin de la liste pour récupérer 10 utilisateurs. Tout cela se fait en n'affichant qu'une seule "fenêtre" de résultats à la fois.
En vérifiant le index
d'un élément donné, un état de chargement différent peut être affiché pour un élément selon qu'une requête a été envoyée pour de nouvelles entrées et que l'élément est toujours en cours de chargement.
Exemple :
const Row = ({ index, style }) => {
const itemLoading = index === items.length;
if (itemLoading) {
// return loading state
} else {
// return item
}
};
Surbalayage
Étant donné que les éléments d'une liste virtualisée ne changent que lorsque l'utilisateur fait défiler l'écran, un espace vide peut clignoter brièvement lorsque de nouvelles entrées sont sur le point d'être affichées. Pour le constater, vous pouvez essayer de faire défiler rapidement l'un des exemples précédents de ce guide.
Pour améliorer l'expérience utilisateur des listes virtualisées, react-window
vous permet de surbalayer les éléments avec la propriété overscanCount
. Cela vous permet de définir le nombre d'éléments à afficher en permanence en dehors de la "fenêtre" visible.
<FixedSizeList
//...
overscanCount={4}
>
{...}
</FixedSizeList>
overscanCount
fonctionne pour les composants FixedSizeList
et VariableSizeList
, et sa valeur par défaut est 1. En fonction de la taille d'une liste et de la taille de chaque élément, le surbalayage de plusieurs entrées peut aider à éviter un clignotement visible d'espace vide lorsque l'utilisateur fait défiler la liste. Toutefois, un surbalayage excessif peut avoir un impact négatif sur les performances. L'intérêt d'utiliser une liste virtualisée est de minimiser le nombre d'entrées à ce que l'utilisateur peut voir à un moment donné. Essayez donc de maintenir le nombre d'éléments surbalayés aussi bas que possible.
Pour FixedSizeGrid
et VariableSizeGrid
, utilisez les propriétés overscanColumnsCount
et overscanRowsCount
pour contrôler respectivement le nombre de colonnes et de lignes à surbalayer.
Conclusion
Si vous ne savez pas par où commencer pour virtualiser les listes et les tableaux dans votre application, suivez ces étapes :
- Mesurez les performances de rendu et de défilement. Cet article explique comment utiliser le compteur FPS dans les outils pour les développeurs Chrome afin d'explorer l'efficacité du rendu des éléments d'une liste.
- Incluez
react-window
pour toutes les longues listes ou grilles qui affectent les performances. - Si certaines fonctionnalités ne sont pas prises en charge dans
react-window
, envisagez d'utiliserreact-virtualized
si vous ne pouvez pas ajouter cette fonctionnalité vous-même. - Encapsulez votre liste virtualisée avec
react-window-infinite-loader
si vous devez charger les éléments de manière différée à mesure que l'utilisateur fait défiler la page. - Utilisez la propriété
overscanCount
pour vos listes et les propriétésoverscanColumnsCount
etoverscanRowsCount
pour vos grilles afin d'éviter un affichage de contenu vide. Ne balayez pas trop d'entrées, car cela aura un impact négatif sur les performances.