Contactez-nous

Gestion des états de chargement (Loading), succès (Success) et erreur (Error)

Apprenez à gérer les états de chargement, succès et erreur pour une meilleure UX lors des appels API en React avec useState et useEffect.

Pourquoi une gestion d'état explicite est cruciale

Lorsque nous effectuons un appel API dans `useEffect`, comme vu précédemment, la simple récupération des données et leur stockage dans l'état ne suffisent pas pour offrir une expérience utilisateur (UX) satisfaisante. Une requête réseau prend du temps, et elle peut soit réussir, soit échouer. L'utilisateur doit être informé de ce qui se passe pendant ces différentes phases.

Sans une gestion explicite des états, l'utilisateur pourrait être confronté à :

  • Une page blanche ou vide pendant le chargement initial, sans aucune indication que quelque chose se passe en arrière-plan.
  • Un affichage soudain du contenu une fois les données arrivées, ce qui peut être déroutant.
  • Aucun retour visuel si la requête échoue, laissant l'utilisateur dans l'incertitude sans savoir pourquoi les données attendues n'apparaissent pas.

Pour pallier cela, il est essentiel d'introduire des variables d'état dédiées pour suivre le cycle de vie de la requête : un état pour indiquer si elle est en cours (chargement / loading), un état pour stocker les données en cas de succès (success), et un état pour capturer les informations en cas d'erreur (error).

Mise en place des variables d'état nécessaires

Nous allons utiliser le hook `useState` pour créer les variables d'état nécessaires à la gestion de ce cycle de vie. Typiquement, nous aurons besoin de trois états :

  • `data` : Pour stocker les données récupérées avec succès. Son état initial dépend du type de données attendu (souvent `null` ou un tableau vide `[]`).
  • `loading` : Un booléen indiquant si la requête est en cours. On l'initialise généralement à `true` si on veut afficher un indicateur de chargement dès le montage, ou à `false` si l'on déclenche la requête plus tard.
  • `error` : Pour stocker l'objet ou le message d'erreur en cas d'échec. Initialisé à `null`.
import React, { useState, useEffect } from 'react';

function MaListeAvecEtats() {
  const [data, setData] = useState(null); // Ou useState([]) si on attend un tableau
  const [loading, setLoading] = useState(true); // Commence en chargement
  const [error, setError] = useState(null);

  // ... useEffect pour le fetch ...

  // ... JSX pour le rendu conditionnel ...
}

Avec ces trois variables, nous pouvons représenter tous les états possibles de notre processus de data fetching.

Intégrer la gestion d'état dans `useEffect`

Modifions notre exemple précédent (avec `fetch`) pour inclure la mise à jour de ces états aux moments appropriés :

import React, { useState, useEffect } from 'react';

function ListeAvecGestionEtats() {
  const [data, setData] = useState([]); // Initialisé comme tableau vide
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      // Important: Réinitialiser l'état au début (si l'effet peut se relancer)
      setLoading(true);
      setError(null);
      // setData([]); // Optionnel: vider les anciennes données pendant le chargement

      try {
        const response = await fetch('https://api.example.com/items');
        if (!response.ok) {
          throw new Error(`Erreur HTTP: ${response.status}`);
        }
        const result = await response.json();
        
        // Succès !
        setData(result);
        // setLoading(false); // Déplacé dans le 'finally' pour plus de robustesse

      } catch (err) {
        // Erreur !
        setError(err.message); // Stocker le message d'erreur
        // setLoading(false); // Déplacé dans le 'finally'
        console.error("Erreur fetch:", err);
      } finally {
        // Ce bloc s'exécute toujours, que la requête réussisse ou échoue
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  // ... Rendu conditionnel basé sur loading, error, data ...
  // (voir section suivante)

  return (
      
/* Le JSX viendra ici */
); } export default ListeAvecGestionEtats;

Points importants :

  • Au début de l'exécution de `fetchData`, nous mettons `loading` à `true` et réinitialisons `error` à `null`.
  • En cas de succès, nous mettons à jour `data` avec `setData(result)`.
  • En cas d'erreur (dans le `catch`), nous mettons à jour `error` avec `setError(err.message)` (ou l'objet `err` complet si nécessaire).
  • L'utilisation du bloc `finally` est une bonne pratique pour s'assurer que `setLoading(false)` est appelé que la promesse réussisse ou échoue. Cela évite la duplication de code dans les blocs `try` et `catch`.

Rendu conditionnel basé sur les états

Maintenant que nous suivons les états `loading`, `error` et `data`, nous pouvons les utiliser dans la partie JSX de notre composant pour afficher l'interface appropriée :

// Suite du composant ListeAvecGestionEtats

  // Logique de rendu conditionnel
  if (loading) {
    return 
Chargement en cours...
; // Afficher un indicateur de chargement } if (error) { return
Erreur : {error}
; // Afficher le message d'erreur } // Si pas de chargement et pas d'erreur, on peut afficher les données // Il est aussi bon de vérifier si data existe ou a une longueur > 0 return (

Liste des éléments

{data.length > 0 ? (
    {data.map(item => (
  • {item.name}
  • ))}
) : (

Aucun élément à afficher.

// Cas où data est un tableau vide )}
); } export default ListeAvecGestionEtats;

L'ordre des conditions est important : on vérifie d'abord `loading`, puis `error`. Si aucune de ces conditions n'est vraie, on peut alors tenter d'afficher les données, en ajoutant éventuellement une vérification supplémentaire pour savoir si des données ont effectivement été reçues (par exemple, `data.length > 0` si `data` est un tableau).

Cette approche de gestion explicite des états de chargement, succès et erreur, combinée au rendu conditionnel, est fondamentale pour créer des interfaces utilisateur robustes et informatives lors de l'interaction avec des API en React. C'est un pattern que vous retrouverez très souvent, bien que les bibliothèques dédiées comme React Query puissent l'abstraire en partie pour vous.