Source: hooks/useFetch.js

import { useEffect, useState } from "react";
import useApp from "./useApp";

/**
 * L'objet retournée par la fonction useFetch
 *
 * @typedef {Object} UseFetchObject
 * @property {any} data
 *    Les Les données récupérées ou la valeur par défaut
 * @property {boolean} isLoading
 *    Indicateur de chargement en cours
 * @property {boolean} isAborted
 *    Indicateur d'annulation du chargement (est déclenché lorsque le
 *    composant est démonté, c'est souvent le cas avec le strict mode,
 *    qui effectue deux montage du composant)
 * @property {?string} error
 *    Contient le message d'erreur ou null si il n'y pas eu d'erreur
 */

/**
 * Hook de gestion du fetch des données en gérant le chargement, les erreurs
 * et les données à récupèrer :
 *
 *   - data      : Les données récupérées ou la valeur par défaut
 *   - isLoading : Indicateur de chargement en cours
 *   - isAborted : Indicateur d'annulation du chargement (est déclenché lorsque le
 *                 composant est démonté, c'est souvent le cas avec le strict mode,
 *                 qui effectue deux montage du composant)
 *   - error : Contient le message d'erreur ou null
 *
 * @param {number} id Identifiant de l'utilisateur
 * @param {function} fetcher Fonction asynchrone pour récupèrer les données (async)
 * @param {string} title Titre informatif des données pour l'affichage des données et des erreurs dans la console
 * @param {*} defaultData Données à utiliser par défaut
 * @returns {UseFetchObject} Un objet
 */
function useFetch(id, fetcher, title, defaultData) {
  const { isMock } = useApp();

  const [data, setData] = useState(defaultData);
  const [isLoading, setIsLoading] = useState(true);
  const [isAborted, setIsAborted] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    const abortController = new AbortController();
    setIsLoading(true);
    fetcher(id, isMock, { signal: abortController.signal })
      .then((data) => {
        setData(data);
        setError(null);
        setIsAborted(false);
        console.log(`Données '${title}':`, data);
      })
      .catch((e) => {
        const isAborted = e instanceof DOMException && e.name === "AbortError";
        if (isAborted) {
          setError(`Annulation du chargement des données !`);
          console.warn(`Annulation du chargement des données '${title}' !`);
        } else {
          setError(`Echec de chargement des données !`);
          console.error(e);
        }
        setIsAborted(isAborted);
      })
      .finally(() => {
        setIsLoading(false);
      });
    return () => {
      abortController.abort();
    };
  }, [id, title, isMock, fetcher]);

  return { data, isLoading, isAborted, error };
}

export default useFetch;