Articles
Sep 18, 2024

useEffect et boucle infinie

Pourquoi j'ai une boucle infinie dans mon useEffect ?

useEffect et boucle infinie

🔄 Pourquoi en React, l’ajout de dĂ©pendances dans useEffect peut provoquer une boucle infinie

Lorsque vous travaillez avec React, il est fréquent d'utiliser le hook useEffect pour gérer des effets secondaires tels que :

  • 🌐 Les requĂȘtes rĂ©seau,
  • 📡 Les abonnements Ă  des Ă©vĂ©nements,
  • đŸ–±ïž La mise Ă  jour de l'Ă©tat.

Cependant, l’ajout de certaines dĂ©pendances dans le tableau de dĂ©pendances de useEffect, comme recommandĂ© par le linter, peut parfois conduire Ă  une boucle infinie.

Cet article explique pourquoi cela se produit et comment Ă©viter ce problĂšme.

1ïžâƒŁ Le fonctionnement de useEffect

useEffect est utilisé pour exécuter du code aprÚs le rendu du composant. Vous pouvez le configurer pour s'exécuter uniquement lors du premier rendu (en fournissant une liste de dépendances vide), ou chaque fois que certaines variables changent.

Voici la structure générale de useEffect :

useEffect(() => {  
 // Code à exécuter
}, [dépendances]);

‍

Le tableau des dĂ©pendances dĂ©termine quand l'effet doit ĂȘtre exĂ©cutĂ© :

  • Si le tableau est vide ([]), l’effet ne s’exĂ©cute qu’une seule fois 🏁.
  • Si vous passez des variables dans le tableau ([var1, var2]), l’effet s’exĂ©cutera chaque fois que l'une de ces variables change 🔄.

2ïžâƒŁ Le rĂŽle du linter 🔍

Les linters, comme ESLint, suggĂšrent souvent d’ajouter des variables utilisĂ©es dans votre useEffect pour garantir que l'effet reflĂšte correctement les changements de ces variables. Cela permet de maintenir le code cohĂ©rent et Ă  jour.

Cependant, suivre aveuglĂ©ment ces recommandations peut parfois provoquer des boucles infinies, oĂč useEffect est rĂ©-exĂ©cutĂ© sans cesse.

Mais alors au lieu de juste faire ça :

  useEffect(() => {
    setSelected(defaultValue);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [defaultValue]);

.......si si, on vous voit 👀

Essayons de comprendre, au moins en surface, pourquoi j'ai une boucle infinie et comment l'Ă©viter.

3ïžâƒŁ Pourquoi l’ajout de dĂ©pendances peut provoquer une boucle infinie 🌀

a. Les fonctions ou objets redĂ©finis Ă  chaque rendu 🚹

En React, les fonctions et objets dĂ©finis Ă  l'intĂ©rieur d'un composant sont recrĂ©Ă©s Ă  chaque rendu. Si vous les incluez comme dĂ©pendances, l’effet sera rĂ©-exĂ©cutĂ© sans cesse, car la rĂ©fĂ©rence change constamment.

Exemple problĂ©matique :‍

const Component = () => {
  const [count, setCount] = useState(0);
  
  const doSomething = () => {
    console.log('Doing something');
  };
  
  useEffect(() => {
    doSomething();
  }, [doSomething]); // ⚠ ProblĂšme : `doSomething` est recrĂ©Ă©e Ă  chaque rendu, boucle infinie};
  
  return <div>{count}</div>;
};

‍

b. Les Ă©tats mis Ă  jour dans useEffect

Mettre à jour un état avec setState dans un useEffect déclenche un nouveau rendu. Si cet état est inclus dans la liste des dépendances, cela provoque une boucle infinie, car le rendu relance l'effet, qui à son tour modifie l'état.

Exemple :

const Component = () => {
  const [data, setData] = useState(null);
  useEffect(() => {
    // Simuler une requĂȘte API
    fetchData().then((response) => {
      setData(response);
    });
  }, [data]); // ⚠ Boucle infinie : `data` est rĂ©actualisĂ©e Ă  chaque rendu};
  return <div>{count}</div>;
};

‍‍

4ïžâƒŁ Comment Ă©viter les boucles infinies ✅

a. Utiliser useCallback ou useMemo pour stabiliser les références

Utilisez useCallback pour stabiliser une fonction ou useMemo pour stabiliser un objet, afin d'éviter qu'ils soient recréés à chaque rendu.

const Component = () => {
  const [count, setCount] = useState(0);

  const doSomething = useCallback(() => {
    console.log("Doing something");
  }, []); // đŸ› ïž Fonction mĂ©morisĂ©e

  useEffect(() => {
    doSomething();
  }, [doSomething]);
};

‍

b. Ne pas inclure les Ă©tats mis Ă  jour dans useEffect

Évitez d'inclure des Ă©tats qui sont mis Ă  jour par l’effet dans la liste des dĂ©pendances, sauf si c'est absolument nĂ©cessaire.‍


const Component = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchData().then(response => {
      setData(response);
    });
  }, []); // ✅ Ne pas inclure `data` pour Ă©viter la boucle
};

‍

c. Analyser la logique mĂ©tier 🧠

Assurez-vous que l'effet doit réellement se déclencher à chaque changement de certaines variables. Sinon, ajustez la liste des dépendances pour n'inclure que les variables nécessaires.

d. Utiliser des conditions dans useEffect

Ajoutez des conditions dans votre effet pour contrĂŽler son exĂ©cution.‍

useEffect(() => {
  if (condition) {
    // Code exécuté uniquement si la condition est vraie
  }
}, [dépendances]);

‍

🚀 Conclusion

Les boucles infinies dans useEffect proviennent souvent de dĂ©pendances instables (comme des fonctions ou des objets recrĂ©Ă©s Ă  chaque rendu) ou de la mise Ă  jour d’états surveillĂ©s inutilement. Pour Ă©viter ces erreurs tout en respectant les recommandations du linter, stabilisez vos rĂ©fĂ©rences avec useCallback ou useMemo, et gĂ©rez soigneusement vos dĂ©pendances dans useEffect.

‍