Apprendre à apprivoiser l'utilisation de ReactCallback Hook
Publié: 2022-04-27Ce n'est un secret pour personne que React.js est devenu très populaire ces dernières années. C'est maintenant la bibliothèque JavaScript de choix pour de nombreux acteurs les plus importants d'Internet, notamment Facebook et WhatsApp.
L'une des principales raisons de son essor a été l'introduction de crochets dans la version 16.8. Les crochets React vous permettent d'exploiter les fonctionnalités de React sans écrire de composants de classe. Désormais, les composants fonctionnels avec des crochets sont devenus la structure incontournable des développeurs pour travailler avec React.
Dans cet article de blog, nous allons approfondir un crochet spécifique - useCallback
- car il touche à une partie fondamentale de la programmation fonctionnelle connue sous le nom de mémorisation. Vous saurez exactement comment et quand utiliser le crochet useCallback
et tirer le meilleur parti de ses capacités d'amélioration des performances.
Prêt? Plongeons-nous !
Qu'est-ce que la mémorisation ?
La mémorisation se produit lorsqu'une fonction complexe stocke sa sortie afin qu'elle soit appelée la prochaine fois avec la même entrée. C'est similaire à la mise en cache, mais à un niveau plus local. Il peut ignorer tous les calculs complexes et renvoyer la sortie plus rapidement car elle est déjà calculée.
Cela peut avoir un effet significatif sur l'allocation de mémoire et les performances, et c'est ce que le crochet useCallback
est censé atténuer.
UseCallback vs useMemo de React
À ce stade, il convient de mentionner que useCallback
marie bien avec un autre hook appelé useMemo
. Nous en discuterons tous les deux, mais dans cet article, nous allons nous concentrer sur useCallback
comme sujet principal.
La principale différence est que useMemo
renvoie une valeur mémorisée, tandis que useCallback
renvoie une fonction mémorisée. Cela signifie que useMemo
est utilisé pour stocker une valeur calculée, tandis que useCallback
renvoie une fonction que vous pouvez appeler ultérieurement.
Ces crochets vous rendront une version mise en cache à moins que l'une de leurs dépendances (par exemple, l'état ou les accessoires) ne change.
Jetons un coup d'œil aux deux fonctions en action :
import { useMemo, useCallback } from 'react' const values = [3, 9, 6, 4, 2, 1] // This will always return the same value, a sorted array. Once the values array changes then this will recompute. const memoizedValue = useMemo(() => values.sort(), [values]) // This will give me back a function that can be called later on. It will always return the same result unless the values array is modified. const memoizedFunction = useCallback(() => values.sort(), [values])
L'extrait de code ci-dessus est un exemple artificiel mais montre la différence entre les deux rappels :
-
memoizedValue
deviendra le tableau[1, 2, 3, 4, 6, 9]
. Tant que la variable values reste, il en va de même pourmemoizedValue
et elle ne sera jamais recalculée. -
memoizedFunction
sera une fonction qui retournera le tableau[1, 2, 3, 4, 6, 9]
.
Ce qui est génial avec ces deux rappels, c'est qu'ils sont mis en cache et traînent jusqu'à ce que le tableau de dépendances change. Cela signifie que sur un rendu, ils ne seront pas ramassés.
tweeterRendu et réaction
Pourquoi la mémorisation est-elle importante lorsqu'il s'agit de React ?
Cela a à voir avec la façon dont React rend vos composants. React utilise un DOM virtuel stocké en mémoire pour comparer les données et décider quoi mettre à jour.
Le DOM virtuel aide React avec les performances et maintient votre application rapide. Par défaut, si une valeur de votre composant change, le composant entier sera restitué. Cela rend React "réactif" à l'entrée de l'utilisateur et permet à l'écran de se mettre à jour sans recharger la page.
Vous ne voulez pas rendre votre composant car les modifications n'affecteront pas ce composant. C'est là que la mémorisation via useCallback
et useMemo
est utile.
Lorsque React restitue votre composant, il recrée également les fonctions que vous avez déclarées dans votre composant.
Notez que lorsque vous comparez l'égalité d'une fonction à une autre fonction, elles seront toujours fausses. Parce qu'une fonction est aussi un objet, elle ne s'égalera qu'à elle-même :
// these variables contain the exact same function but they are not equal const hello = () => console.log('Hello Matt') const hello2 = () => console.log('Hello Matt') hello === hello2 // false hello === hello // true
En d'autres termes, lorsque React restitue votre composant, il verra toutes les fonctions déclarées dans votre composant comme étant de nouvelles fonctions.
C'est très bien la plupart du temps, et les fonctions simples sont faciles à calculer et n'affecteront pas les performances. Mais les autres fois où vous ne voulez pas que la fonction soit considérée comme une nouvelle fonction, vous pouvez compter sur useCallback
pour vous aider.
Vous pensez peut-être : "Quand est-ce que je ne voudrais pas qu'une fonction soit considérée comme une nouvelle fonction ?" Eh bien, il y a certains cas où useCallback
plus de sens :
- Vous passez la fonction à un autre composant qui est également mémorisé (
useMemo
) - Votre fonction a un état interne dont elle doit se souvenir
- Votre fonction est une dépendance d'un autre hook, comme
useEffect
par exemple
Avantages en termes de performances de React useCallback
Lorsque useCallback
est utilisé de manière appropriée, il peut aider à accélérer votre application et empêcher les composants de se restituer s'ils n'en ont pas besoin.
Disons, par exemple, que vous avez un composant qui récupère une grande quantité de données et est responsable de l'affichage de ces données sous la forme d'un tableau ou d'un graphique, comme ceci :
Supposons que le composant parent du composant de votre visualisation de données effectue un nouveau rendu, mais que les accessoires ou l'état modifiés n'affectent pas ce composant. Dans ce cas, vous ne voulez probablement pas ou n'avez pas besoin de le restituer et de récupérer toutes les données. Éviter ce nouveau rendu et cette nouvelle récupération peut économiser la bande passante de votre utilisateur et offrir une expérience utilisateur plus fluide.
Inconvénients de React useCallback
Bien que ce crochet puisse vous aider à améliorer les performances, il comporte également ses pièges. Certaines choses à considérer avant d'utiliser useCallback
(et useMemo
) sont :
- Garbage collection : Les autres fonctions qui ne sont pas déjà mémorisées seront jetées par React pour libérer de la mémoire.
- Allocation de mémoire : Semblable à la récupération de place, plus vous avez de fonctions mémorisées, plus vous aurez besoin de mémoire. De plus, chaque fois que vous utilisez ces rappels, il y a un tas de code à l'intérieur de React qui doit utiliser encore plus de mémoire pour vous fournir la sortie mise en cache.
- Complexité du code : lorsque vous commencez à encapsuler des fonctions dans ces crochets, vous augmentez immédiatement la complexité de votre code. Il faut maintenant mieux comprendre pourquoi ces crochets sont utilisés et confirmer qu'ils sont utilisés correctement.
Être conscient des pièges ci-dessus peut vous éviter de tomber dessus vous-même. Lorsque vous envisagez d'utiliser useCallback
, assurez-vous que les avantages en termes de performances l'emporteront sur les inconvénients.
Réagir useCallback Exemple
Vous trouverez ci-dessous une configuration simple avec un composant Button et un composant Counter. Le compteur a deux éléments d'état et affiche deux composants Button, chacun mettant à jour une partie distincte de l'état des composants du compteur.
Le composant Button prend deux props : handleClick
et name. Chaque fois que le bouton est rendu, il se connecte à la console.
import { useCallback, useState } from 'react' const Button = ({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> } const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) return ( <> {countOne} {countTwo} <Button handleClick={() => setCountOne(countOne + 1)} name="button1" /> <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" /> </> ) }
Dans cet exemple, chaque fois que vous cliquez sur l'un ou l'autre des boutons, vous verrez ceci dans la console :
// counter rendered // button1 rendered // button2 rendered
Maintenant, si nous appliquons useCallback
à nos fonctions handleClick
et encapsulons notre Button dans React.memo
, nous pouvons voir ce que useCallback
nous fournit. React.memo
est similaire à useMemo
et nous permet de mémoriser un composant.
import { useCallback, useState } from 'react' const Button = React.memo(({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> }) const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne) const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]) return ( <> {countOne} {countTwo} <Button handleClick={memoizedSetCountOne} name="button1" /> <Button handleClick={memoizedSetCountTwo} name="button1" /> </> ) }
Désormais, lorsque nous cliquons sur l'un des boutons, nous ne voyons que le bouton sur lequel nous avons cliqué pour nous connecter à la console :
// counter rendered // button1 rendered // counter rendered // button2 rendered
Nous avons appliqué la mémorisation à notre composant de bouton, et les valeurs de prop qui lui sont transmises sont considérées comme égales. Les deux fonctions handleClick
sont mises en cache et seront considérées comme la même fonction par React jusqu'à ce que la valeur d'un élément du tableau de dépendances change (par exemple countOne
, countTwo
).
Résumé
Aussi cool que useCallback
et useMemo
soient, rappelez-vous qu'ils ont des cas d'utilisation spécifiques - vous ne devriez pas envelopper chaque fonction avec ces crochets. Si la fonction est complexe en termes de calcul, une dépendance d'un autre crochet ou un accessoire passé à un composant mémorisé sont de bons indicateurs que vous voudrez peut-être atteindre pour useCallback
.
Nous espérons que cet article vous a aidé à comprendre cette fonctionnalité avancée de React et vous a aidé à gagner en confiance avec la programmation fonctionnelle en cours de route !