Les React Hooks

Les hooks de React sont apparus dans la version 16.7.0 alpha et sont des fonctions qui permettent d’utiliser toutes les fonctionnalités des classes React dans des composants fonctionnels. On peut grâce aux hooks développer une application React uniquement faite des composants fonctionnels. Il est toutefois intéressant de noter que les composants fonctionnels utilisant les hooks sont tout à fait compatibles avec des classes React.
Si vous désirez passer aux hooks sur votre application, vous pouvez le faire au fur-et-à-mesure sans casser le reste de votre application. Ces hooks permettent entre autre de remplacer le state et les méthodes de cycle de vie d’une classe React. Comme il ne s’agit pas de classes, le mot clé ‘this’ n’a plus d’intérêt dans votre code.
Il y a deux règles à savoir pour utiliser les hooks :

  1. Les hooks sont appelés au plus haut niveau d’un composant. C’est à dire que vous ne pouvez pas les appeler depuis une fonction ou une sous-fonction.
  2. Les hooks sont appelés uniquement dans des composants fonctionnels. Ils ne fonctionneront pas dans une classe.

Pour vous aider et vous guider dans vos développements, vous pouvez utiliser le linter ESlint grâce à la règle suivante : eslint-plugin-react-hooks
 

#1 UseState

useState est le premier hook que nous allons découvrir. Il permet de remplacer le fameux state d’une classe React. Il se définit avec son setter sur une ligne :

Qu’avons nous fait ici
La fonction useState retourne un tableau. Cette fonction prend comme paramètre la valeur initiale désirée.
Le premier élément du tableau (name) est une variable donnant accès à la valeur d’état (ici UX-Republic).
Le deuxième élément (setName) est une fonction qui met à jour l’état du composant afin d’avoir les nouvelles valeurs dans le DOM.
L’exemple suivant permet de voir la transformation d’une classe React vers un composant fonctionnel utilisant le hook useState : 

Observons le code :
Plus besoin du constructeur, la définition de la variable name et de son setter dans le useState le remplace. On ne retrouve plus de ‘this’ non plus dans le code pour accéder aux données du composant.
Enfin, pour modifier la valeur de ‘name’ il suffit d’appeler le setName définit dans le useState, ici aussi moins de code est nécessaire qu’avec les classes React.
UseState est probablement le hook le plus simple proposé par React. 

#2 UseEffect

UseEffect est le second hook que nous abordons dans cet article.Il permet de remplacer les méthodes de cycle de vie d’une classe React.
Dans une classe, les méthode de cycle de vie sont déclenchées (comme leur nom l’indique) aux étapes du cycle de vie du composant. Le hook useEffect est déclenché principalement par les données envoyées à ce composant. 
La classe React nous impose d’utiliser ses méthodes.
Or, des logiques métier différentes d’un composant vont parfois se retrouver dans une même méthode telle que componentWillReceiveProps. Ce qui complexifie la compréhension des méthodes.
UseEffect offre la possibilité de séparer la logique métier et facilite la lecture d’un composant. Comment s’y prend-il ? Il est possible de créer autant de méthodes useEffect qu’on le désire dans un composant. Nous reviendrons là-dessus après quelques exemples.

#3 useEffect basique

Dans un premier exemple nous allons voir comment utiliser useEffect dans un simple composant qui modifie le titre de la page quand on clique sur un bouton. Voici ce que donnerait une classe React qui fait cela.

devient

On voit que l’utilisation de useEffect simplifie le composant et remplace les deux méthodes de cycle de vie. Cette méthode est appelée lors de l’initiation du composant. De plus, chaque fois que le composant se mettra à jour, la méthode useEffect sera appelée. On ne s’intéresse plus vraiment au cycle de vie du composant ici. On lui indique juste qu’à chaque mise à jour, il doit passer dans la méthode useEffect.

#4 useEffect avec effet cleanup

Nous allons maintenant découvrir l’effet cleanup. C’est une fonctionnalité du useEffect qui permet d’effectuer une action lorsque le composant unmount. Pour cela il suffit d’ajouter un return d’une fonction que vous voulez exécuter lors de l’unmount dans le useEffect.
Voici un exemple :

On voit dans ces lignes de code que lorsque le composant s’initialise, on souscrit au statut d’un ami via le ChatAPI afin de savoir s’il est en ligne ou non. On retourne ce résultat via la méthode handleStatusChange et on le rend dans le render. Afin d’éviter une fuite de mémoire, lors de l’unmount du composant, on unsubscribe au statut de notre ami via le ChatAPI. 
L’effet cleanup peut nous aider ici à simplifier ce code avec useEffect :

Ici on déplace la fonction handleStatusChange dans le useEffect car elle n’est pas utilisé hors de son scope. On souscrit au statut comme lors du componentDidMount. Enfin on a ajouté dans le return une fonction cleanup dans laquelle on unsubscribe du statut de notre ami. Une fois encore on gagne en place et toute la logique est regroupée au même endroit.

#5 Bien utiliser le useEffect

Voici quelques astuces pour bien utiliser le useEffect de React Hooks :

  • il est préférable d’utiliser plusieurs useEffect pour séparer la logique et rendre le code plus lisible. Il est intéressant de noter que les useEffect sont lancés dans l’ordre dans lequel ils sont écrit. 
  • on peut optimiser les performances en passant des effects. useEffect se lance à chaque mise à jour du composant.

Parfois vous ne voudrez passer dans le useEffect que si certaines valeurs dans votre composant changent. Plutôt que d’utiliser des boucles if dans la fonction useEffect pour tester la mise à jour de certaines variable, vous pouvez passer comme second argument au useEffect un tableau avec les variables à observer. Dès que l’une de ces variables changera, la fonction useEffect sera appelée.
Voici un exemple :

Ici on a une boucle if que l’on ne veut pas transposer vers les hooks.

Ce tableau que l’on passe en paramètre du useEffect contient les données qu’il écoute pour lancer son code. Si ces données changent, alors il exécute son code, sinon il ne fait rien. On peut le voir comme une méthode willReceiveProps dédiée à certaines props. Dans l’exemple ci-dessus, le titre du document est mis à jour lorsque count change de valeur.
Pour info, vous pouvez passer un tableau vide à useEffect, dans ce cas, le code sera exécuté uniquement lors de la création du composant et ne repassera plus dedans lors des mises à jour de ce composant.
 

Pour aller plus loin

Vous pouvez créer vos propres hooks si vous le désirez. Pour cela rien de plus simple, il suffit de définir une fonction dont le nom commence par ‘use’ et que cette fonction utilise des hooks de React. On trouve déjà de nombreux custom hooks sur github.
Si lors de l’utilisation de useState, on constate que la méthode d’initialisation de la variable prend beaucoup de temps, on peut faire du lazy initialization facilement. C’est à dire initialiser sa variable de manière asynchrone. 
 
Souvenez-vous, useState prend en paramètre la valeur par défaut d’une variable. Nous pouvons retarder l’initialisation de la variable définie dans le useState jusqu’au moment où on en a vraiment besoin en passant une fonction en paramètre du useState.
Voici un exemple d’implémentation :

Ici, l’initialisation de ‘valeur’ se fera de manière asynchrone et n’impactera pas la vitesse d’affichage du reste de la page.
 

Conclusion

Avec les hooks on peut se débarrasser des classes de React, du mot clé this et de toutes les lignes nécessaires à la création d’un composant (le constructeur, le binding des fonctions…). On peut isoler les différentes logiques qui interviennent dans un même composant et améliorer la lisibilité de celui-ci. 
Avec des hooks custom on peut découper un composant en davantage de fonctions qu’avec les classes. 
Il est également plus facile de tester notre composant car la logique à tester est localisées et plus facile à comprendre (car non entrecoupée par d’autre logique).
De plus, avec ce découpage on peut facilement tester chaque partie sans craindre d’impact sur les autres fonctions.
Personnellement, je trouve cela plus logique de penser son composant en fonction de ce qu’il reçoit comme props qu’en fonction d’un cycle de vie. Chaque composant peut être optimisé en évitant de passer dans certains hooks. 
Dans l’ensemble on a besoin de moins de lignes de codes pour arriver à un même résultat tout en gardant une grande lisibilité du code.
 
Biblio
https://reactjs.org/docs/hooks-intro.html
https://putaindecode.io/fr/articles/js/react/react-hooks/
https://medium.com/@dan_abramov/making-sense-of-react-h ooks-fdbde8803889
https://medium.freecodecamp.org/an-introduction-to-react-hooks-12843fcd2fd9
https://codeburst.io/react-hooks-in-5-minutes-1180f1aa0449
https://blog.bitsrc.io/understanding-react-hooks-usestate-6627120614ab
 
Valentin Dupetitpre, JS Craftsman @UX-Republic