Partie 1 : Découvrir React
Partie 2 : Acquérir les bases de React
Partie 3 : Aller plus loin avec React
Partie 4 : Bonus
Partie 5 : Obtenir votre sticker React

4.1. Le Hook d’effet

Ce Hook d’effet se nomme useEffect. Il a pour rôle de vous permettre d’utiliser les méthodes de cycle de vie dans vos composants de fonctions.

Pour être plus explicite, le Hook d’effet permet de remplacer les méthodes de cycle de vie suivantes : componentDidMount, componentDidUpdate et componentWillUnmount. Pour la plupart des applications, ces méthodes sont suffisantes pour couvrir la majorité des besoins d’implémentation.

Par exemple, imaginons un composant qui affiche une très belle liste de fruits, mais les fruits ne sont disponibles que sur un serveur distant, et ne sont donc pas disponible de base dans notre composant. Il faut donc que nous utilisions le Hook useEffect pour résoudre notre problème :

  • Dans notre composant de fonction, initialiser un state vide, qui devra contenir nos fruits.
  • Au montage du composant, appeler le serveur distant pour recevoir la liste de fruits. (ComponentDidMount)

Et oui, je vais parler de fruits tout le long de ma démonstration. Pourquoi pas ? 🍑🍏🍌🍐🍍

Voyons tout de suite comment nous pouvons implémenter tout cela, avec des Hooks :

// Tiens tiens, une nouvelle importation...
import React, { FunctionComponent, useState, useEffect } from 'react';
import { FruitsAPI, Fruit, currentUserId }  from './quelque-part';

const Fruits: FunctionComponent = () => {
 // Le fameux 'state'.
 const [fruits, setFruits] = useState<Fruit[]>([]);
 
 // Ah, le Hook d'effet !
 useEffect(() => {
  FruitsAPI.getFruits(currentUserId).then(fruits => setFruits(fruits));
 })

 return (
  <h1>Vous avez {fruits.length} dans votre panier.</h1>
 );
}

export default App;

Alors, qu’est ce que nous avons dans ce code…

D’abord à la ligne 1, nous importons le Hook useEffect depuis le paquet React, comme pour n’importe quel Hook. Nous importons également les éléments fictifs suivants : FruitsAPI pour effectuer des requêtes au serveur distant, le modèle Fruit représentant un fruit dans notre application, et enfin currentUserId correspondant à l’identifiant unique de l’utilisateur courant.

Ensuite à la ligne 7, nous mettons en place un state avec une variable d’état nommé fruits. Nous initialisons ce state avec un tableau vide. Mais ça, nous connaissons déjà.

Enfin, de la ligne 10 à 12, nous utilisons le Hook useEffect. Ce Hook prend en argument une fonction, qui sera exécuté au même moment que les méthodes componentDidMount et componentDidUpdate seraient appelées. Cette fonction utilise FruitsAPI pour récupérer les données nécessaires à l’affichage des fruits dans notre composant. Nous utilisons ensuite la méthode setFruits que nous avons récupéré précédemment depuis le Hook d’état useState. Tout ça !

Heu, d’accord, mais on ne vas pas avoir un problème de performance par rapport à componentDidUpdate ?

Effectivement, à chaque fois qu’un élément de notre interface va être modifié dans un hypothétique futur, nous allons nous trouver à faire un appel à l’API de fruits. Le mieux serait de ne faire un appel à l’API de fruits qu’une seule fois, à l’initialisation de notre composant. Alors, nous allons voir comment faire cela, il faut ….

Attend, et comment gérer le cas du désabonnement ? On a vu que 2 méthodes du cycle de vie là !

Alors, attendez, cela fait beaucoup de questions d’un coup. Je pense que le mieux est de vous montrer trois exemples différents qui traitent tous les cas possibles, et qui accessoirement répondront à vos questions actuels :

// CAS 1 : Je veux juste récupérer des données initiales, et c'est tout !
 useEffect(() => {
  FruitsAPI.getFruits(currentUserId).then(fruits => setFruits(fruits));
 }, [])

// CAS 2 : Je veux récupérer des données initiales, et ensuite mettre à jour mon interface s'il y a des modifications pertinentes.
 useEffect(() => {
  FruitsAPI.getFruits(currentUserId).then(fruits => setFruits(fruits));
 }, [currentUserId])


// CAS 3 : Je veux reprendre le cas n°2, mais j'aimerai effectuer un traitement spécifique au démontage du composant. 
 useEffect(() => {
  FruitsAPI.subscribeToFruits(currentUserId, doSomething());

  return () => {
   FruitsAPI.unsubscribeFromFruits(currentUserId, doSomething()); // traitement spécifique
  }
 }, [currentUserId])

Dans le cas °1, on récupère notre liste de fruits au démarrage du composant, et c’est tout. Pour cela, on passe un deuxième argument au Hook d’effet, il s’agit d’un tableau vide. Cela empêche l’appel ultérieur au Hook, après le chargement du composant. Cela revient à appeler la méthode componentDidMount.

Dans le cas n°2, on indique que l’on veut continuer à mettre à jour notre affichage. Pour éviter d’envoyer des requêtes inutiles à notre serveur, et ralentir notre application, on passe en deuxième argument un tableau avec la variable currentUserId à l’intérieur. Cela revient à dire à React « Ok, tu ne fait l’appel au serveur que la valeur de currentUserId est modifiée, sinon il n’y a pas besoin de mettre à jour l’affichage« . Ainsi, on a un composant à jour, et une performance optimale. Cela permet de combiner les méthodes componentDidMount et componentDidUpdate.

Dans le cas 3, non seulement on initialise notre composant et on le maintient à jour, mais on aimerai en plus effectuer un traitement supplémentaire juste avant que le composant soit démonté. Pour cela, vous devez ajouter une fonction de retour à votre Hook d’effet. Dans notre cas, on se désabonne de notre API de fruits, puisque nous n’avons plus besoin de maintenir la connexion à cette API. Cela revient à combiner les méthodes componentDidMount, componentDidUpdate et componentWillUnmount.

Maintenant, je pense que c’est plus clair pour vous. Vous devriez mieux comprendre comment utiliser le Hook d’effet pour combiner ensemble les méthodes de cycle de vie.

Un des avantages principaux, c’est que le code d’une même fonctionnalité d’un composant n’est pas séparé en trois méthodes de cycle de vie différentes, mais rassemblé au même endroit. De plus, la syntaxe est beaucoup plus concise avec les Hooks, et évite de multiples oublis ou erreurs. 👍