Menu

Error boundary React : intercepter les erreurs de rendu

Une error boundary est un composant qui intercepte les erreurs levées pendant le rendu des composants qu'il contient et affiche un contenu de repli au lieu d'un écran blanc. Découvrez ce qu'elle intercepte, comment l'écrire sous forme de classe, comment la réinitialiser, et la bibliothèque react-error-boundary.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Une error boundary est un composant React qui intercepte les erreurs levées pendant le rendu des composants qu'il contient et affiche une interface de repli au lieu de faire planter toute l'application. Ce doit être un composant classe qui définit static getDerivedStateFromError, car il n'existe pas de hook pour cela. Vous l'écrivez une fois et l'enroulez autour de n'importe quelle partie de l'arbre.

Appuyez sur "Break the profile" pour faire lever une erreur à l'enfant pendant le rendu.

Le profil est remplacé par le contenu de repli, et le reste de la page continue de fonctionner. La console affiche l'erreur deux fois : React affiche chaque erreur interceptée avec console.error, et componentDidCatch l'affiche à nouveau. "Try again" efface la cause (broken) et l'erreur de la boundary, donc le profil s'affiche à nouveau. Retirez this.props.onReset(); de reset et Try again renvoie aussitôt au contenu de repli, car rien n'a corrigé la cause.

Comment fonctionne la classe

Deux méthodes font d'une classe une error boundary :

  • static getDerivedStateFromError(error) s'exécute pendant le rendu, juste après qu'un enfant a levé une erreur. Elle renvoie la mise à jour d'état qui fait passer la boundary à son contenu de repli. Elle doit être pure : pas de journalisation, pas de requêtes.
  • componentDidCatch(error, info) s'exécute une fois le contenu de repli à l'écran. Utilisez-la pour les effets de bord comme l'envoi de l'erreur à un service de suivi. info.componentStack liste le composant qui a levé l'erreur et les composants au-dessus.
class ErrorBoundary extends Component {
    state = { error: null };

    static getDerivedStateFromError(error) {
        return { error }; // switch to the fallback
    }

    componentDidCatch(error, info) {
        reportError(error, info.componentStack); // your logging service
    }

    render() {
        if (this.state.error) return this.props.fallback;
        return this.props.children;
    }
}

Il n'existe pas d'équivalent en hook, donc même une application écrite uniquement avec des composants fonctions a besoin de cette unique classe. La page sur les composants classes explique la syntaxe des classes si elle est nouvelle pour vous.

Ce qu'une boundary intercepte, et ce qu'elle n'intercepte pas

Une boundary intercepte les erreurs qui se produisent pendant que React travaille sur les composants qu'elle contient :

  • le rendu (le corps d'un composant fonction, le render d'une classe),
  • les méthodes de cycle de vie et les constructeurs des composants classes enfants,
  • les effets (useEffect, useLayoutEffect) et les ref callbacks,
  • une promesse rejetée lue avec use et, dans React 19, une erreur levée par une action de formulaire ou par une fonction passée au startTransition de useTransition.

Elle n'intercepte pas :

  • les erreurs dans les gestionnaires d'événements,
  • le code async que vous lancez vous-même : setTimeout, les callbacks .then(), un await dans un gestionnaire d'événement,
  • les erreurs dans le propre render de la boundary (c'est la boundary suivante au-dessus qui les intercepte),
  • les erreurs pendant le rendu serveur.

Les gestionnaires d'événements sont exclus parce qu'ils ne cassent pas le rendu. L'écran reste dans un état valide, donc un try/catch dans le gestionnaire suffit. Si vous voulez quand même que la boundary s'affiche, stockez l'erreur dans l'état et levez-la au rendu suivant :

Le premier bouton laisse les boutons à l'écran et affiche seulement une erreur non interceptée dans la console. Le second intercepte le rejet, le stocke, et la boundary prend le relais.

Où placer les boundaries

Une seule boundary autour de toute l'application évite l'écran blanc, mais alors la moindre erreur remplace tout. Placez des boundaries autour des parties qui peuvent échouer indépendamment : un widget de barre latérale, un graphique, chaque route. Un échec coûte alors un panneau à l'utilisateur, pas la page.

Seul le panneau Stocks affiche son contenu de repli. Placez les deux widgets dans une seule <ErrorBoundary> et le panneau Weather, qui fonctionne, disparaît avec lui.

Sans aucune boundary, React retire tout l'arbre de l'écran quand une erreur de rendu n'est pas interceptée. Une page vide est plus sûre qu'une page cassée qui affiche de fausses données, mais les utilisateurs la perçoivent comme un plantage.

La bibliothèque react-error-boundary

Le paquet react-error-boundary enveloppe la classe pour vous et ajoute des utilitaires de réinitialisation. Il n'est pas disponible dans cet éditeur, le voici donc dans un bloc statique :

import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';

function Fallback({ error, resetErrorBoundary }) {
    return (
        <div role="alert">
            <p>Something went wrong: {error.message}</p>
            <button onClick={resetErrorBoundary}>Try again</button>
        </div>
    );
}

<ErrorBoundary FallbackComponent={Fallback} onReset={() => refetch()} resetKeys={[userId]}>
    <Profile userId={userId} />
</ErrorBoundary>;

// Inside a child: hand an async error to the boundary
const { showBoundary } = useErrorBoundary();
fetchData().catch(showBoundary);

resetKeys réinitialise la boundary quand l'une des valeurs listées change, et showBoundary applique l'astuce « stocker puis lever » de l'exemple précédent.

Signaler les erreurs dans React 19

React 19 signale les erreurs d'une nouvelle façon. Les erreurs interceptées par une boundary sont affichées une seule fois avec console.error, et les erreurs non interceptées vont à window.reportError. Pour les envoyer à votre propre service, passez des options à createRoot. Chaque option remplace la journalisation par défaut correspondante :

import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'), {
    onCaughtError(error, errorInfo) {
        // an error boundary caught it
        logToService('caught', error, errorInfo.componentStack);
    },
    onUncaughtError(error, errorInfo) {
        // no boundary caught it, and React removed the tree
        logToService('uncaught', error, errorInfo.componentStack);
    },
    onRecoverableError(error, errorInfo) {
        // React recovered by itself, for example after a hydration mismatch
        logToService('recovered', error, errorInfo.componentStack);
    },
});
root.render(<App />);

onRecoverableError sert aux erreurs que React a corrigées sans votre aide, comme une incohérence d'hydratation ou une erreur de rendu qui a disparu quand React a réessayé. hydrateRoot accepte les trois mêmes options.

Erreurs courantes

Placer la boundary dans le composant qui lève l'erreur. Une boundary n'intercepte que les erreurs de ses enfants. Si Profile affiche une <ErrorBoundary> autour de son propre JSX puis lève une erreur dans son propre corps, l'erreur passe outre et va à la boundary suivante au-dessus. Enveloppez le composant depuis son parent.

Des effets de bord dans getDerivedStateFromError. Elle s'exécute pendant le rendu et peut s'exécuter plus d'une fois, limitez-la donc à renvoyer un état. La journalisation a sa place dans componentDidCatch ou dans le onCaughtError de la racine.

Une réinitialisation qui ne corrige rien. Effacer l'erreur de la boundary affiche les mêmes enfants avec les mêmes props. S'ils lèvent à nouveau une erreur, l'utilisateur revient au contenu de repli. Changez d'abord ce qui a causé l'erreur (nouvelle récupération, effacement d'une saisie invalide, changement de key), puis effacez l'erreur, comme le fait le premier exemple avec onReset.

Une seule boundary pour toute l'application, et rien d'autre. Elle évite la page blanche mais transforme chaque petit échec en erreur pleine page. Ajoutez des boundaries autour des fonctionnalités, et gardez celle du niveau supérieur comme dernier recours.

Des contenus de repli qui vous cachent l'erreur. Un message aimable est ce qu'il faut pour les utilisateurs, mais assurez-vous que l'erreur arrive quand même dans vos journaux. React 19 affiche par défaut les erreurs interceptées dans la console ; en production, cette console est celle de votre utilisateur, envoyez donc les erreurs à un service depuis componentDidCatch ou onCaughtError.

Les error boundaries s'associent naturellement à <Suspense> : Suspense gère « pas encore prêt », la boundary gère « a échoué ». Placez la boundary à l'extérieur du Suspense, comme dans les exemples avec use, pour qu'un seul composant couvre le chargement, le succès et l'échec.

Questions fréquentes

Qu'est-ce qu'une error boundary dans React ?

Un composant qui intercepte les erreurs JavaScript levées pendant le rendu de n'importe quel composant qu'il contient, et affiche à la place une interface de repli. Sans elle, une erreur pendant le rendu retire toute l'application de l'écran.

Existe-t-il un hook pour les error boundaries ?

Non. Une error boundary doit être un composant classe avec static getDerivedStateFromError (et éventuellement componentDidCatch). Vous l'écrivez une fois et l'utilisez depuis des composants fonctions comme n'importe quel autre composant, ou vous installez react-error-boundary, qui enveloppe la classe pour vous.

Les error boundaries interceptent-elles les erreurs des gestionnaires d'événements ?

Non. Les gestionnaires d'événements s'exécutent en dehors du rendu, donc React n'a pas besoin d'une boundary pour s'en remettre. Utilisez try/catch dans le gestionnaire, et si vous voulez que la boundary s'affiche, stockez l'erreur dans l'état et levez-la pendant le rendu.

Les error boundaries interceptent-elles les erreurs async ?

Pas celles de setTimeout, des callbacks de promesses ou des fetch().then() que vous lancez vous-même. Elles interceptent une promesse rejetée lue avec use et, dans React 19, une erreur levée dans une action de formulaire ou dans une fonction passée au startTransition de useTransition.

Comment réinitialiser une error boundary ?

Effacez son état d'erreur, généralement depuis un bouton Réessayer dans le contenu de repli, après avoir corrigé ce qui a causé l'erreur. Changer la key de la boundary la réinitialise aussi, car React monte une nouvelle instance.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

COMMENCER