Guide complet sur le hook d'effet useEffect

Le 03 novembre 2021

UseEffect React

Une explication simple du React.useEffect()

Je suis impressionné par l'expressivité des hooks React. Vous pouvez faire tellement de choses en écrivant si peu.

Mais la brièveté des hooks a un prix - ils sont relativement difficiles à appréhender. En particulier useEffect() - le hook qui gère les effets de bord dans les composants React fonctionnels.

Dans ce billet, vous apprendrez comment et quand utiliser le hook useEffect().

1 - useEffect() est pour les effets de bord

Un composant fonctionnel React utilise des props et/ou un état pour calculer la sortie. Si le composant fonctionnel effectue des calculs qui ne ciblent pas la valeur de sortie, ces calculs sont appelés effets secondaires.

Des exemples d'effets secondaires sont les demandes de récupération, la manipulation directe du DOM, l'utilisation de fonctions de temporisation comme setTimeout(), etc.

Le rendu du composant et la logique des effets secondaires sont indépendants. Ce serait une erreur d'effectuer des effets secondaires directement dans le corps du composant, qui est principalement utilisé pour calculer la sortie.

La fréquence de rendu du composant n'est pas quelque chose que vous pouvez contrôler - si React veut rendre le composant, vous ne pouvez pas l'arrêter.

function Greet({ name }) {
  const message = `Hello, ${name}!`; // Calculates output
  // Bad!
  document.title = `Greetings to ${name}`; // Side-effect!
  return <div>{message}</div>; // Calculates output
}

Comment découpler le rendu de l'effet secondaire ? Bienvenue à useEffect() - le hook qui exécute les effets secondaires indépendamment du rendu.

import { useEffect } from 'react';
function Greet({ name }) {
  const message = `Hello, ${name}!`; // Calculates output
  useEffect(() => {
    // Good!
    document.title = `Greetings to ${name}`; // Side-effect!
  }, [name]);
  return <div>{message}</div>; // Calculates output
}

le hook useEffect accepte deux arguments - useEffect(callback[, dependencies]);

  • callback est la fonction contenant la logique de l'effet de bord. La callback est exécutée juste après que les changements aient été poussés vers le DOM.
  • dependencies est un tableau optionnel de dépendances. useEffect() exécute la callback seulement si les dépendances ont changé entre les rendus.

Placez votre logique d'effet secondaire dans la fonction de rappel, puis utilisez l'argument dépendances pour contrôler quand vous voulez que l'effet secondaire s'exécute. C'est le seul but de useEffect().

Schema useEffect

Par exemple, dans l'extrait de code précédent, vous avez vu la fonction useEffect() en action :

useEffect(() => {
  document.title = `Greetings to ${name}`;
}, [name]);

La mise à jour du titre du document est un effet secondaire car elle ne calcule pas directement la sortie du composant. C'est pourquoi la mise à jour du titre du document est placée dans une callback et fournie à useEffect().

De plus, il n'est pas nécessaire que la mise à jour du titre du document soit exécutée à chaque fois que le composant Greet est rendu. Vous souhaitez simplement qu'elle soit exécutée lorsque le prop de nom change - c'est la raison pour laquelle vous avez fourni name comme dépendance à useEffect(callback, [name]).

2. Le tableau de dépendances en argument

L'argument dependencies de useEffect(callback, dependencies) vous permet de contrôler quand l'effet de bord s'exécute. Lorsque les dépendances sont :

2.1 Quand le tableau de dépendances n'est pas donné

Lorsque le tableau de dépendances n'est pas donné, l'effet de bord sera déclenché à chaque rendu

import { useEffect } from 'react';
function MyComponent() {
  useEffect(() => {
    // Runs after EVERY rendering
  });
}

2.2 Quand un tableau vide est passé en argument

Lorsque un tableau vide est passé en argument, le rendu s'effectue une seule fois, après le rendu initial.

import { useEffect } from 'react';
function MyComponent() {
  useEffect(() => {
    // Runs ONCE after initial rendering
  }, []);
}

2.3 Quand des dépendances sont spécifiées dans le tableau

Lorsque des valeurs sont spécifiées, l'effet de bord sera déclenché lorsque à chaque fois qu'une valeur passé dans le tableau de dépendance est modifiée.

import { useEffect, useState } from 'react';
function MyComponent({ prop }) {
  const [state, setState] = useState('');
  useEffect(() => {
    // Runs ONCE after initial rendering
    // and after every rendering ONLY IF `prop` or `state` changes
  }, [prop, state]);
}

Regardons en détail l'utilsiation des versions 2.1 et 2.3

3. Cycle de vie des composants.

3.1 ComponentDidMount Est-ce que le composant est monté

Utilisez un tableau de dépendances vide pour invoquer un effet bord une fois après le montage du composant :

import { useEffect } from 'react';
function Greet({ name }) {
  const message = `Hello, ${name}!`;
  useEffect(() => {
    // Runs once, after mounting
    document.title = 'Greetings page';
  }, []);
  return <div>{message}</div>;
}

useEffect(..., []) était fourni avec un tableau vide comme argument de dépendances. Lorsqu'elle est configurée de cette manière, la fonction useEffect() n'exécute le callback qu'une seule fois, après le montage initial.

Même si le composant est rendu à nouveau avec une propriété de nom différente, l'effet secondaire ne s'exécute qu'une seule fois après le premier rendu :

    // First render
    <Greet name="Eric" />   // Side-effect RUNS
    // Second render, name prop changes
    <Greet name="Stan" />   // Side-effect DOES NOT RUN
    // Third render, name prop changes
    <Greet name="Butters"/> // Side-effect DOES NOT RUN

3.1 ComponentDidUpdate Est-ce que le composant est mis à jour ?

Chaque fois que l'effet de bord utilise des props ou des valeurs d'état, vous devez indiquer ces valeurs comme des dépendances :

import { useEffect } from 'react';
function MyComponent({ prop }) {
  const [state, setState] = useState();
  useEffect(() => {
    // Side-effect uses `prop` and `state`
  }, [prop, state]);
  return <div>....</div>;
}

La fonction useEffect(callback, [prop, state]) invoque le callback après que les modifications ont été enregistrées dans le DOM et si et seulement si une valeur du tableau de dépendances [prop, state] a changé.

En utilisant l'argument dependencies de useEffect(), vous contrôlez quand invoquer l'effet secondaire, indépendamment des cycles de rendu du composant. Encore une fois, c'est l'essence même du hook useEffect().

Améliorons le composant Greet en utilisant la proposition de nom dans le titre du document :

import { useEffect } from 'react';
function Greet({ name }) {
  const message = `Hello, ${name}!`;
  useEffect(() => {
    document.title = `Greetings to ${name}`;
  }, [name]);
  return <div>{message}</div>;
}

name prop est mentionné dans l'argument dependencies de useEffect(..., [name]). Le hook useEffect() exécute l'effet secondaire après le rendu initial, et lors des rendus ultérieurs seulement si la valeur du nom change.

4. La fonction de nettoyage

a fonction de nettoyageest un élément complexe qui mérite son article dédié.

5. useEffect en pratique

5.1 Récupérer des données

useEffect() peut effectuer un effet secondaire de récupération de données.

Le composant suivant, FetchEmployees, récupère la liste des employés sur le réseau :

import { useEffect, useState } from 'react';
function FetchEmployees() {
  const [employees, setEmployees] = useState([]);
  useEffect(() => {
    async function fetchEmployees() {
      const response = await fetch('/employees');
      const fetchedEmployees = await response.json(response);
      setEmployees(fetchedEmployees);
    }
    fetchEmployees();
  }, []);
  return (
    <div>
      {employees.map((name) => (
        <div>{name}</div>
      ))}
    </div>
  );
}

useEffect() lance une requête de récupération en appelant la fonction asynchrone fetchEmployees() après le montage initial.

Lorsque la requête est terminée, setEmployees(fetchedEmployees) met à jour l'état employees avec la liste d'employés qui vient d'être récupérée.

Notez que l'argument callback de useEffect(callback) ne peut pas être une fonction asynchrone. Mais vous pouvez toujours définir et invoquer une fonction asynchrone dans le callback lui-même :

function FetchEmployees() {
  const [employees, setEmployees] = useState([]);
  useEffect(() => {
    // <--- CANNOT be an async function
    async function fetchEmployees() {
      // ...
    }
    fetchEmployees(); // <--- But CAN invoke async functions
  }, []);
  // ...
}

Pour exécuter la requête de récupération en fonction d'une valeur de prop ou d'état, il suffit d'indiquer la dépendance requise dans l'argument dépendances : useEffect(fetchSideEffect, [prop, stateValue]).

6. Conclusion

useEffect(callback, dependencies) est le hook qui gère les effets secondaires dans les composants fonctionnels. L'argument callback est une fonction pour mettre la logique de l'effet secondaire. dependencies est une liste de dépendances de votre effet secondaire : étant des props ou des valeurs d'état.

useEffect(callback, dependencies) invoque la callback après le montage initial, et lors des rendus ultérieurs, si une valeur dans les dependencies a changé.

L'étape suivante pour maîtriser useEffect() est de comprendre et d'éviter le piège de la boucle infinie.