Salut les furieux du clavier,

Si vous avez eu l’occasion d’utiliser le hook useEffect, vous devez savoir qu’il peut donner des résultats inattendus dans certains cas – ce qui peut être source de vilains bugs. Je suis moi-même passé par là et c’est pourquoi j’aimerais vous donner les règles à respecter pour maîtriser à 100% ce hook et vous éviter des séances de grattage de tête.

Sommaire :

1) useEffect, kézako ?
2) useEffect, niveau 2 : la fonction clean up
3) useEffect, niveau 3 : les dépendances
4) Les règles à suivre pour utiliser useEffect

useEffect, kézako ?

Pour ceux n’ayant pas eu l’occasion de faire joujou avec ce hook, ou bien n’en aurait même jamais entendu parler, un bref rappel de sa fonction. useEffect vous permet de déclencher un effet de bord lors du re-rendering d’un composant. Sur le papier, ça parait simple.

Petit exemple basique. Un composant contenant un useState qui va déclencher l’affichage d’un log dans la console de développeur  :

const MonComposant = () => {
  const [shouldLog, setShouldLog] = useState(false);

  useEffect(() => {
    if (shouldLog) {
      console.log('Coucou !');
    }
  });

  return (
    <button onClick={() => setShouldLog(true)}>Générer un log</button>
  );
};

Ci-dessus, lorsque l’on clique sur le bouton “Générer un log”, shouldLog passe à true et notre chaine de caractères Coucou ! est affichée dans la console.

useEffect, niveau 2 : la fonction clean up

Le hook useEffect permet de retourner une fonction de clean up qui nous permet de nettoyer après que notre effet de bord ait été exécuté.

Un exemple simple pour illustrer cette fonction de nettoyage :

useEffect(() => {
  console.log('Coucou !');
  
  // On retourne ici notre fonction de nettoyage
  return () => console.clear();
});

Dans l’exemple ci-dessus, notre chaine de caractères “Coucou !” est affichée dans la console à chaque re-render, et la méthode de clean up va venir clear la console avant de ré-exécuter le useEffect.

useEffect, niveau 3 : les dépendances

Dans l’exemple précédent, on a un useEffect simple, qui sera déclenché à chaque re-render de notre composant. L’utilisation du deuxième paramètre du hook useEffect va nous permettre de faire une utilisation plus fine de cet effet de bord “contrôlé” : la liste de dépendances de notre hook.
Ces dépendances sont des valeurs que le useEffect va étudier à chaque re-render. Si les valeurs listées ne sont pas modifiées, le useEffect ne sera pas déclenché. Si au contraire, la valeur d’une des dépendances listées a changé le useEffect sera bien déclenché.

Reprenons notre premier exemple, en ajoutant ce coup-ci le deuxième paramètre dans notre useEffect ainsi qu’un autre useState qui viendra causer un re-render de notre composant :

const MonComposant = () => {
  const [shouldLog, setShouldLog] = useState(false);
  const [isWhatever, setIsWhatever] = useState(false);

  useEffect(() => {
    // N'est jamais appelé lors que l'utilisateur clique sur "Rafraîchir"
    if (shouldLog) {
      console.log('Coucou !');
    }
  }, [shouldLog]);

  return (
    <div>
      <button onClick={() => setShouldLog(true)}>Afficher</button>
      <button onClick={() => setIsWhatever(!isWhatever)}>Rafraîchir</button>
    </div>
  );
};

Ici, le fait d’avoir listé dans le deuxième paramètre de notre useEffect la dépendance shouldLog fait que le code à l’intérieur n’est appelé que si shouldLog change. Si l’utilisateur clique sur le bouton “Rafraîchir”, rien ne sera affiché dans la console.

Les règles à suivre pour utiliser useEffect

Mais que se passe-t-il quand plusieurs dépendances sont listées ? Et bien si une des dépendances a changé au moment d’un re-render, le useEffect est déclenché. C’est aussi simple que ça. Mais c’est aussi ici qu’est le risque principal de ce hook. Trop de dépendances tuent les dépendances.

La première règle à suivre est la suivante : créez un useEffect pour chaque effet de bord que vous souhaitez implémenter. Ne tentez jamais de factoriser deux useEffects en un : c’est la première cause de bugs qui j’ai moi-même observée.

La deuxième règle : utilisez le plugin ESLint hooks React et suivez scrupuleusement les warnings qu’il vous remontera. La plupart du temps les indications que ce plugin vous fera seront à suivre sans se poser de question.

Je ne vois qu’un seul cas de figure où vous pourrez vous en passer – et désactiver ESLint sur la ligne affichée en warning dans votre éditeur. Lorsque vous souhaitez implémenter un effet de bord au premier rendering de votre composant ou bien avant que le composant soit retiré du DOM :

useEffect(() => {
  console.log('Premier rendering !');
// Ici je ne passe pas volontairement pas de dépendances au useEffect pour
// qu'il ne soit déclenché qu'une seule fois, au premier rendering du
// composant.
}, []);