Comment bien gérer les erreurs avec Remix ? (ErrorBoundary)
Tags: webdev, javascript, react, remix
Published: 2024-05-25 09:59:49

As-tu déjà eu des erreurs Javascript en production ? **Pire** encore, as-tu reçu l'appel d'un client, qui se plaint d'avoir reçu un message d'erreur ?
Tu perds de la crédibilité.

Heureusement, il existe le composant _ErrorBoundary_ qui permet d'afficher un composant d'erreur, respectant le thème du site et ton Design System.

{% youtube https://youtu.be/ZcOywCf4shU %}

## Avant d'utiliser ErrorBoundary

Tu retrouves un message d'erreur générique incompréhensible. Difficile de comprendre ce qui a pu se passer, et tu vas prendre du temps à réparer le bug. De plus, ton site est inutilisable en l'état. La navigation de ton utilisateur a été coupée brutalement.

![Erreur générique Javascript](https://algomax-public.s3.eu-west-3.amazonaws.com/3_Ul0_S_e9785a9f36.png)

## Après avoir mis en place ErrorBoundary

Tu améliores l'expérience utilisateur. Tu réduis l'impact de l'erreur à l'endroit où elle a été déclenchée. Cela permet au développeur de vite résoudre le problème, car il sait exactement de quel fichier il s'agit.

Dans Remix, on utilise le composant [ErrorBoundary](https://remix.run/docs/en/main/route/error-boundary), qui va remplacer le composant ayant déclenché l'erreur.

![Message d'erreur Remix localisé, clair et concis, ne perturbant pas le parcours utilisateur](https://algomax-public.s3.eu-west-3.amazonaws.com/error_boundary_cdcf793439.png)

Tu retrouves cet exemple et davantage d'information dans la documentation de Remix concernant [l'error handling](https://remix.run/docs/en/main/guides/errors)

Pour améliorer l'expérience utilisateur (UX), il est recommandé d'offrir une explication claire dans le message d'erreur, comme expliqué dans la figure ci-dessous ([Source](https://www.linkedin.com/pulse/designing-better-error-messages-ux-vitaly-friedman/)).

![Erreur Javascript personnalisée, rassurant le client et expliquant ce qui s'est passé](https://algomax-public.s3.eu-west-3.amazonaws.com/1692716312806_e0b0f10037.png)

Un message d'erreur idéal :

- **rassure** le client
- **explique** la raison du problème
- **offre** une marche à suivre pour arranger les choses (contacter le support)

## Qu'est-ce que le composant ErrorBoundary ?

C'est une _librairie_ Javascript, disponible sur _NPM_ sous le nom de [react-error-boundary](https://www.npmjs.com/package/react-error-boundary). Elle est téléchargée plus de 3,7 millions de fois par semaine.

Pour l'utiliser dans une application React standard, il suffit de la télécharger dans ton projet :

```bash
npm install react-error-boundary
```

Ensuite, tu l'utilises dans un composant React. Elle va _englober_ le composant qui risque de déclencher une erreur, et afficher un composant _fallback_ à la place.

```tsx
import { ErrorBoundary } from 'react-error-boundary';

<ErrorBoundary fallback={<div>Something went wrong</div>}>
	<ExampleApplication />
</ErrorBoundary>;
```

Cet exemple montre deux composants :

- Le composant parent _ErrorBoundary_, qui prend un _children_ (le composant enfant) et une propriété _fallback_ (affichant le composant d'erreur)
- Le composant enfant _ExampleApplication_. En cas d'erreur de ce dernier, l'erreur ne va pas remonter à la racine de l'application. À la place, l'erreur va afficher le composant _fallback_.

Il y a une deuxième manière d'utiliser ce composant, offrant plus de contrôle et une meilleure UX.

```tsx
import { ErrorBoundary } from 'react-error-boundary';

function fallbackRender({ error, resetErrorBoundary }) {
	// Call resetErrorBoundary() to reset the error boundary and retry the render.

	return (
		<div role='alert'>
			<p>Something went wrong:</p>
			<pre style={{ color: 'red' }}>{error.message}</pre>
		</div>
	);
}

<ErrorBoundary
	fallbackRender={fallbackRender}
	onReset={(details) => {
		// Reset the state of your app so the error doesn't happen again
	}}
>
	<ExampleApplication />
</ErrorBoundary>;
```

La propriété _fallbackRender_ accepte une fonction fléchée, nous permettant de récupérer deux arguments :

- Les informations sur l'erreur qui a été déclenchée
- Une fonction permettant de ré-effectuer un rendu du composant. Déclencher cette fonction permet d'annuler l'erreur, et d'afficher le composant enfant.

Généralement, on laisse l'utilisateur appeler cette méthode `resetErrorBoundary`. Plus haut, par exemple en utilisant le bouton _Try Again_.

Mais cet article ne se concentre pas sur ce composant très utile.

Si tu utilises [Remix](https://remix.run), tu n'as même pas besoin de l'utiliser.

Il est intégré directement dans la librairie !

## Comment gérer tes erreurs avec Remix

Pour déclencher une erreur dans ton application Remix, il te suffit d'ajouter l'instruction `throw new Error('bam')` sur n'importe quelle route active.

Ce qui affiche ceci :

![Une erreur dans une application Remix](https://algomax-public.s3.eu-west-3.amazonaws.com/image_19_c7ce872c07.png)

Cette erreur n'est pas claire du tout. Et elle rend ton application inutilisable pour les clients : Ils ne peuvent pas accéder à une autre page depuis l'interface.

De plus, cela ne t'aide pas à connaître la raison de l'erreur : elle pourrait être déclenchée par n'importe quoi.

Comment faire ? Il suffit d' **exporter un composant nommé ErrorBoundary** dans ton application Remix. Commence par l'importer dans le fichier `root.tsx`, se situant dans le dossier `app/routes` de ton projet Remix.

## Le composant ErrorBoundary (Remix)

Le composant _ErrorBoundary_ est une convention de Remix (au même titre que les méthodes _loader_ et _action_). Exporter un composant nommé "ErrorBoundary" dans n'importe quelle route indique à Remix qu'en cas d'erreur, il doit afficher son contenu, et ne pas propager l'erreur plus haut.

Prenons par exemple l'export ci-dessous :

```tsx
export function ErrorBoundary() {
    const error = useRouteError();
    let errorMessage = 'Une erreur inattendue est survenue';

    if (error instanceof Error) {
        errorMessage = error.message;
    }

    return <div>{errorMessage}</span>;
}
```

Après avoir ajouté ces quelques lignes, notre application ressemble à ça :

![Notre application Remix affiche le message d'erreur "bam"](https://algomax-public.s3.eu-west-3.amazonaws.com/image_20_66ae48cf4e.png)

On avait rajouté la ligne `throw new Error('bam');`. Au lieu d'afficher l'erreur en rouge, sans design, notre composant _ErrorBoundary_ est affiché.

Cela nous apporte quelques bénéfices :

- L'erreur ne se propage pas au dessus (on y revient un peu plus bas)
- On peut designer notre message d'erreur, expliquer au client ce qui s'est passé, et lui indiquer la marche à suivre
- En bonus, on peut sauvegarder cette erreur avec un outil comme _Sentry_, ou l'envoyer par email pour être prévenu si jamais elle est déclenchée en production.

## Les erreurs ne remontent pas

[React](https://react.dev) utilise un système de composants, avec des balises imbriquées. Ce qui donne une hiérarchie avec des "parents" et des "enfants".

Avec **Remix**, il est possible d'implémenter des _routes imbriquées_. Cela signifie qu'il est possible d'avoir plusieurs routes _actives_ en même temps.

C'est ce que nous montre l'image, que nous avions vu au dessus :

![Message d'erreur Remix localisé, clair et concis, ne perturbant pas le parcours utilisateur](https://algomax-public.s3.eu-west-3.amazonaws.com/error_boundary_cdcf793439.png)

Cette image représente une route imbriquée. Quatres routes sont actuellement actives :

- La route _root_ principale. C'est notre composant d'entrée, cette route est rendue sur toutes les routes. La route _root_ affiche la barre latérale, sur le côté gauche.
- La route _sales_ (on la voit comme premier segment dans l'URL) affiche le titre de la page **"Sales"**, ainsi qu'une barre de navigation horizontale.
- La route _invoices_ (on retrouve son segment dans l'URL) est "enfant" de la route Sales, et affiche les statistiques globales, la barre jaune et verte ainsi que le tableau de factures
- La route dynamique _102000_ (où **102000** représente une _id_ de facture) est imbriquée dans _invoices_ (encore une fois, on le constate grâce au segment d'URL final). C'est le dernier enfant de cette hiérarchie, l'enfant le plus bas. Et il est en erreur.

On peut symboliser cette vue d'une autre manière :

```tsx
export default function Page () {
return (<Sales>
      <Invoices>
        <Invoice id="102000"/>
      </Invoices
  </Sales>)
}
```

Chaque imbrication représente un enfant dans notre composant. Et le composant **ErrorBoundary** empêche notre erreur de remonter plus haut.

Ce qui est représenté par l'image ci-dessus, au final, est un message d'erreur qui a empêché une erreur déclenchée dans le composant `Invoice` de remonter plus haut.

C'est un peu comme s'il s'était passé la chose suivante :

```tsx
export default function Page() {
	return (
		<Sales>
			<Invoices>
				<ErrorBoundary fallback={<Error message='Something went wrong' />}>
					<Invoice id='102000' />
				</ErrorBoundary>
			</Invoices>
		</Sales>
	);
}
```

Remix a remplacé le composant `Invoice` par un composant `Error`, car ce fichier était protégé par une fonction ErrorBoundary.

En ajoutant ce composant sur notre route imbriquée, nous améliorons considérablement l'expérience de nos utilisateurs. De plus, nos développeurs identifient plus rapidement le problème, car le composant en erreur est au plus bas dans la hiérarchie.