frontend / react
Error Boundaries
Explication
Ce que vous allez apprendre
- Comprendre pourquoi une erreur de rendu non gérée peut faire planter toute l'application
- Écrire un Error Boundary avec
getDerivedStateFromErroretcomponentDidCatch - Placer plusieurs boundaries ciblées pour isoler les pannes zone par zone
- Identifier précisément ce qu'un Error Boundary ne capture jamais
- Utiliser la librairie
react-error-boundarypour un fallback avec reset
Dans quel contexte ?
Un widget météo tiers intégré dans src/components/WeatherWidget.jsx lève occasionnellement une exception quand l'API externe renvoie une réponse malformée. Sans protection, cette erreur fait disparaître toute la page — y compris le tableau de bord principal, totalement indépendant du widget météo — et affiche un écran blanc à l'utilisateur. Envelopper uniquement ce widget dans son propre <ErrorBoundary> confine la panne à ce widget seul, le reste de l'application continuant de fonctionner normalement.
Étape 1 : le problème sans Error Boundary
Par défaut, une erreur JavaScript non gérée levée pendant le rendu d'un composant ne reste pas confinée à ce composant : elle fait s'effondrer tout l'arbre qui l'englobe.
Étape 2 : la conséquence concrète
Un simple bug dans un petit widget secondaire peut ainsi rendre toute l'application inutilisable, jusqu'à afficher une page blanche à l'utilisateur.
Étape 3 : la solution, un Error Boundary
Un Error Boundary est un composant spécial qui intercepte les erreurs survenant pendant le rendu de ses descendants, et affiche à la place une interface de secours.
Étape 4 : pourquoi c'est encore une class component
Fait notable : à ce jour, il n'existe aucun hook équivalent, ce qui reste l'un des rares cas où une class component est réellement nécessaire.
Étape 5 : les deux méthodes qui s'y combinent
getDerivedStateFromError met à jour l'état pour afficher le fallback au rendu suivant. componentDidCatch s'exécute après coup, typiquement pour envoyer l'erreur à un service de suivi.
Étape 6 : ce qu'un Error Boundary ne capture jamais
C'est le point le plus important : il ne capture QUE les erreurs de rendu de ses descendants, jamais celles d'un gestionnaire d'événement ou d'une promesse rejetée dans un effet.
Piège fréquent
Un try/catch classique reste indispensable dans les handlers d'événements et les effets asynchrones : un Error Boundary ne verra jamais une erreur levée dans un onClick ou dans un .catch() de fetch, même si le composant est bien enveloppé.
| Type d'erreur | Capturée par un Error Boundary ? |
|---|---|
| Erreur pendant le rendu d'un composant descendant | Oui |
Erreur dans un handler onClick/onChange | Non — try/catch manuel requis |
Promesse rejetée dans un useEffect | Non — .catch() manuel requis |
| Erreur dans l'Error Boundary lui-même | Non |
Une bonne pratique à retenir
Placer plusieurs boundaries ciblées à différents endroits permet d'isoler les pannes zone par zone, pour qu'une erreur dans un widget secondaire n'affecte jamais le reste.
Et ensuite ?
Une fois la robustesse assurée, la prochaine étape revient sur un sujet déjà abordé côté HTML : l'accessibilité, appliquée spécifiquement à React.
Commandes & code
Error Boundaries
Empêcher qu'une erreur dans un composant ne fasse planter TOUTE l'application.
// Error Boundary : DOIT être une class component -- pas d'équivalent hook natif à ce jour
import { Component } from "react";
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { aUneErreur: false, erreur: null };
}
// déclenché pendant le rendu -- met à jour le state pour afficher le fallback au prochain rendu
static getDerivedStateFromError(erreur) {
return { aUneErreur: true, erreur };
}
// déclenché APRÈS le rendu -- effet de bord : logger l'erreur vers un service externe
componentDidCatch(erreur, infos) {
console.error("Erreur capturée :", erreur, infos.componentStack);
envoyerVersSentry(erreur, infos);
}
render() {
if (this.state.aUneErreur) {
return this.props.fallback ?? <p>Une erreur est survenue.</p>;
}
return this.props.children;
}
}// Utilisation : englober des zones précises, pas forcément toute l'app d'un bloc
function App() {
return (
<div>
<Header /> {/* si Header plante, tout le reste de l'app disparaît aussi -- hors boundary */}
<ErrorBoundary fallback={<PageErreurGenerique />}>
<TableauDeBord />
</ErrorBoundary>
<ErrorBoundary fallback={<p>Widget indisponible</p>}>
<WidgetMeteoTiers /> {/* un composant tiers instable, isolé, n'affecte pas le reste */}
</ErrorBoundary>
</div>
);
}// Ce qu'un Error Boundary NE capture PAS -- limitations importantes à connaître
// 1. Erreurs dans des handlers d'événements (try/catch classique nécessaire)
function Bouton() {
function gererClic() {
try {
operationRisquee();
} catch (erreur) {
console.error(erreur); // Error Boundary ne verra JAMAIS cette erreur
}
}
return <button onClick={gererClic}>Cliquer</button>;
}
// 2. Erreurs asynchrones (setTimeout, promesses non attendues dans un effet)
useEffect(() => {
fetch("/api/donnees").catch((erreur) => {
// idem : à gérer manuellement, un Error Boundary ne l'attrape pas
console.error(erreur);
});
}, []);
// 3. Erreurs côté serveur (SSR) -- nécessitent leur propre gestion selon le framework
// 4. Erreurs DANS l'Error Boundary lui-même// react-error-boundary : librairie utilitaire courante, avec reset et hooks
import { ErrorBoundary } from "react-error-boundary";
function FallbackErreur({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>Erreur : {error.message}</p>
<button onClick={resetErrorBoundary}>Réessayer</button>
</div>
);
}
function App() {
return (
<ErrorBoundary
FallbackComponent={FallbackErreur}
onReset={() => window.location.reload()}
onError={(erreur, infos) => envoyerVersSentry(erreur, infos)}
>
<TableauDeBord />
</ErrorBoundary>
);
}Résumé
- Un Error Boundary est nécessairement une class component (
getDerivedStateFromError+componentDidCatch). - Il capture les erreurs de rendu des descendants, jamais celles des handlers d'événements ou du code asynchrone.
- Placer plusieurs boundaries ciblées isole les pannes plutôt que de faire disparaître toute l'application.
react-error-boundaryajoute un mécanisme de reset et une API en composant fonction, pratique en production.
Exercices pratiques
Mission : contenir la panne du widget météo sans y arriver vraiment
Objectif : Diagnostiquer pourquoi un Error Boundary posé au bon endroit ne capture pas certaines erreurs, puis corriger le placement et la gestion manquante.
Contexte
Un développeur enveloppe <WeatherWidget /> dans un <ErrorBoundary> comme le recommande la leçon. Pourtant, deux bugs distincts continuent de faire planter toute l'application : le premier survient dans un onClick du widget (operationRisquee() sans try/catch), le second dans un useEffect du widget qui appelle fetch("/api/meteo").catch(...). Le développeur est convaincu que son ErrorBoundary est mal écrit puisqu'il "ne capture rien".