Le Hook d'optimisation des performances useCallback dans React
Dans cette leçon, nous examinerons le Hook suivant
pour l'optimisation des performances :
useCallback.
Le Hook useCallback est similaire à l'API useMemo.
La différence réside dans le fait que le premier
met en cache une valeur entre les rendus,
tandis que le second met en cache une fonction de rappel.
Cela nous permet de ne pas recalculer des fonctions
coûteuses en ressources lorsque ce n'est pas nécessaire et peut
être utilisé lors de la
transmission d'une fonction
aux composants enfants.
Analysons cela plus en détail avec un exemple.
Commençons par créer un composant App
et définissons un état num à l'intérieur :
const [num, setNum] = useState(0);
Ajoutons un bouton sur lequel un clic
augmente num
de 1, et un paragraphe dans lequel nous
afficherons la valeur de num :
return (
<div>
<button onClick={() => setNum(num + 1)}>click</button>
<p>clicks: {num}</p>
</div>
);
Supposons maintenant que nous affichons également
une liste d'éléments dans App, que nous compléterons
en cliquant sur un autre bouton. Pour stocker
les éléments de cette liste, nous allons définir
un état items :
const [items, setItems] = useState([]);
Ensuite, écrivons une fonction addItem
pour les ajouter :
function addItem() {
setItems([...items, 'new item']);
}
Maintenant, écrivons le code pour afficher
les éléments de la liste et déplaçons-le dans un composant enfant
Items, qui recevra en tant que props le tableau d'éléments
et la fonction pour les ajouter. N'oublions pas
d'ajouter un affichage dans la console pour voir
quand notre Items sera
rerendu :
function Items({ items, addItem }) {
const result = items.map((item, index) => {
return <p key={index}>{item}</p>;
});
console.log('Items render');
return (
<div>
<h3>Our items</h3>
{result}
<button onClick={addItem}>add item</button>
</div>
);
}
export default Items;
Plaçons Items à la fin du composant
App et passons-lui le tableau
items et la fonction d'ajout d'éléments
addItem :
return (
<>
<div>
<button onClick={() => setNum(num + 1)}>click</button>
<p>clicks: {num}</p>
<br />
</div>
<Items items={items} addItem={addItem} />
</>
);
Maintenant, cliquons sur les boutons
et assurons-nous que num augmente et
que de nouveaux éléments sont ajoutés à la liste.
En ouvrant la console, nous verrons que
notre liste est rerendue à chaque
fois, même si nous cliquons sur le bouton
qui augmente num.
Si notre liste est petite, tout va bien.
Mais si elle est volumineuse et contient beaucoup d'autres éléments ?
Ce n'est pas grave - direz-vous, car dans la leçon
précédente, nous avons examiné l'API memo,
précisément pour éviter les rendus inutiles
du composant.
Alors, enveloppons notre composant
Items dans memo et le tour est joué.
D'ailleurs, cela peut être fait directement
à l'exportation de Items :
export default memo(Items);
N'oublions pas d'importer memo :
import { memo } from 'react';
Ouvrons maintenant la console et cliquons
sur les boutons. Tous nos efforts sont vains ! Nous
avons mémorisé le composant, mais en cliquant
sur le bouton 'click', le composant
Items est tout de même
rerendu à chaque fois.
Le problème est que lorsque le composant parent
est rerendu, ses fonctions
sont recréées - cela concerne également notre
fonction addItem, que nous transmettons à
Items.
C'est précisément à ce moment que le Hook
useCallback nous aidera. Appliquons-le.
Commençons par l'importer dans
App :
import { useCallback } from 'react';
Ensuite, transformons la simple déclaration de fonction
addItem en une
Expression de Fonction, spécifions comme
premier paramètre de useCallback
notre fonction sous forme de rappel. Le deuxième
paramètre, entre crochets, indiquera
les dépendances - toutes les variables réactives
intervenant dans la fonction, dans notre cas
c'est le tableau items :
const addItem = useCallback(() => {
setItems(() => [...items, 'New item']);
}, [items]);
Terminé ! Ainsi, nous avons mis la fonction en cache.
Cliquons à nouveau sur les boutons et
constatons que maintenant, en cliquant sur le bouton
'click', notre composant enfant ne
se rerend plus.
Créez un composant App, placez-y
un paragraphe avec du texte. Définissez
un état avec la valeur initiale 'text'
et affichez-le dans le paragraphe. Faites en sorte qu'au clic
sur le paragraphe, un point d'exclamation
soit ajouté à la fin du texte.
Créez un composant enfant Products,
dans lequel vous aurez un bouton pour ajouter
un nouveau produit. Placez-le dans App.
Dans le composant parent, créez un état
avec un tableau de produits et une fonction d'ajout
d'un nouveau produit. Transmettez-les en
tant que props au composant enfant, affichez-y
le tableau transmis sous forme de liste ul.
Dans Products, affichez dans la console le texte
'products render'.
Enveloppez Products dans memo.
Cliquez sur le paragraphe et le bouton. Assurez-vous
qu'au clic sur le paragraphe, le composant enfant
est tout de même rerendu.
Mettez en cache la fonction d'ajout de
produits en l'enveloppant dans le Hook useCallback.
Cliquez sur le paragraphe et le bouton. Assurez-vous
qu'au clic sur le paragraphe, le composant enfant
ne se rerend plus.