Loupe

Présentation du hook useCallback

Bonjour !

On se retrouve pour parler hooks et plus précisément d’un hook présent de base dans React depuis la 16.8, à savoir : useCallback.

Avant de commencer, si vous n’êtes pas familier avec le concept de hooks dans React ou que souhaitez en savoir plus sur les hooks en général, vous pouvez jeter un coup d’oeil ici.

Ce hook a pour objectif de ne pas déclencher de nouveaux rendus lorsque vous passez un callback dans un composant enfant.

Pour clarifier simplement, React déclenche un nouveau rendu quand le state ou les props d’un composant changent.

Ainsi, lorsque vous faites ceci :

const MyParentComponent = () => {
	return (
		<MyChildComponent
			label="tutu"
			onChange={(newLabel) => console.log(newLabel)}
		/>
	);
}

Lors d’un nouveau rendu du composant MyParentComponent, React n’est pas en mesure de déterminer si le callback passé dans la prop onChange du composant MyChildComponentest identique au callback du rendu précédent. React considère ainsi cela comme une nouvelle valeur, et déclenche donc un nouveau rendu du composant MyChildComponent .

A noter que c’est exactement la même chose si vous décidez de faire ceci :

const MyParentComponent = () => {

	const handleOnChange = (newLabel) => {
		console.log(newLabel);
	}

	return (
		<MyChildComponent
			label="tutu"
			onChange={handleOnChange}
		/>
	);
}

Afin de résoudre ce problème, React a créé un hook nommé useCallback. Si vous êtes familier avec le terme de mémoïsation, c’est tout simplement ce que fait useCallback en mémoïsant un callback. Ainsi ce hook permet de déclarer une fonction callback qui nous retourne la fonction créée dans une variable comme ceci :

const handleOnChange = useCallback((newLabel) => {
	console.log(newLabel);
}, []);

useCallback prend en premier paramètre votre fonction callback et en deuxième paramètre une liste de dépendances similaires à ce qu’il peut y avoir pour un useEffect.

Ce qui se passe en interne, c’est qu’un appel à useCallback va créer une fonction et la retourner dans une variable.
Lors d’un appel successif à useCallback, et si la liste des dépendances n’a pas changé par rapport au précédent appel, la fonction ne sera pas recalculée et la même adresse sera ainsi retournée.
React a désormais un moyen de savoir si le callback calculé est une nouvelle valeur ou non et ainsi déclencher un nouveau rendu si cela est nécessaire.
Dans la documentation, React conseille donc de passer en dépendance toute variables et fonction utilisée dans le callback à l’exception évidemment des paramètres de la fonction.

Ainsi, si vous souhaitez par exemple avoir un callback qui prend en paramètre une string et retourne l’index de la première occurrence de cette string dans un tableau passé en props au composant, et tout cela en utilisant la fonction indexOf de lodash :

import indexOf from 'lodash/indexOf';

const MyParentComponent = ({ myArray }) => {

	const handleOnChange = useCallback((newLabel) => {
		return indexOf(myArray, newLabel);
	}, [myArray, indexOf]);

	return (
		<MyChildComponent
			label="tutu"
			onChange={handleOnChange}
		/>
	);
}

Ainsi, seulement lorsque la valeur de myArray changera, le callback sera recalculé et déclenchera un nouveau rendu du composant MyChildComponent. En ce qui concerne la fonction indexOf de lodash, la fonction étant un import d’une dépendance externe, il n’y a aucune chance que l’adresse d’indexOf change - cependant React demande à ce que toutes les dépendances, même externes au composant, soient listées dans la liste des dépendances de useCallback.

Pour information, l’équipe derrière React est consciente du problème de devoir spécifier l’ensemble des symboles utilisés dans un tableau de dépendances. Ils espèrent qu’avec l’arrivée future d’un compilateur suffisamment avancé, ce tableau de dépendances pourra être créé automatiquement.

Si vous êtes déjà un utilisateur du plugin rules-of-hooks pour ESLint, le plugin vous indiquera si vous avez oublié une dépendance ou si vous en avez spécifié une inutile. Pour cela, vous devez activer la règle exhaustive-deps du plugin rules-of-hooks.

Et voilà, ce petit changement au niveau de l’utilisation des callbacks devrait vous éviter bon nombre de rendus inutiles et ainsi améliorer sensiblement les performances de votre application React !

Happy Coding !

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus