#CestFacile : React Error Boundaries

La sortie de Fiber (React 16.x)a été l’occasion pour l’équipe React d’apporter un certain nombre de nouveautés à React. D’un côté avec une grosse optimisation du rendu basé sur RequestAnimationFrame, mais aussi d’un point de vue fonctionnel avec les Portals, Fragments, Error Boundary et très bien la nouvelle api Context.

On va découvrir ici en quoi consistent les Error Boundaries, et en quoi ça peut nous être utile au quotidien dans une application React.

Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed.

Tout est dit, merci de votre attention.

Le concept autour des Error Boundaries est de permettre d’intercepter les erreurs des composants dans lesquelles ils sont encapsulés. L’idée étant de pouvoir continuer à utiliser l’application là où les composants fonctionnent encore, tout en informant l’utilisateur qu’il y a un pépin sur certains d’entre eux.

Un exemple vaut mieux qu’on long discours.

Supposons que, dans notre formulaire de login, pour une raison ou pour une autre, un des champ provoque une erreur. On souhaite alors informer l’utilisateur qu’il y a une erreur et que le formulaire n’est pas accessible pour le moment.

Deux possibilités :

  • #1: Soit on encapsule notre formulaire dans un composant ErrorBoundary, ce qui signifie que tous les composants encapsulés et qui pourraient lever une erreur rendront le formulaire totalement inaccessible.
  • #2 : Soit on encapsule chaque champ de formulaire indépendamment, ce qui signifie que les champs ne levant pas d’erreur continueront à s’afficher (ce qui ne servira pas à grand chose dans la vraie vie, mais bon, ça reste un exemple.

Le composant ErrorBoundary

Avant de pouvoir utiliser cette fonctionnalité, on créé notre composant :

import React, {Component} from 'react'

export default class ErrorBoundary extends Component{
 constructor(props){
   super(props)
   this.state = {
     error: null, 
     errorInfo: null
   }
 }

 componentDidCatch(error, errorInfo) {
   this.setState({
     error: error,
     errorInfo: errorInfo
    })
 }

 render(){
  const {error, errorInfos} = this.state;
  if( error ){
   return <div>
            <h2>Huho, quelque chose cloche ! </h2>
            <p>{this.state.error && this.state.error.toString()}</p>
            <br />
            <pre>{this.state.errorInfo.componentStack}</pre>
          </div> 
  } else { 
    return this.props.children
  }
 }
}

Dans ./src/Components/ErrorBoundary/ErrorBoundary.js

On remarque ici que l’on utilise une nouvelle méthode du cycle de vie de React :

componentDidCatch

C’est cette méthode qui va catch les erreurs levée par les composants enfants et agir en conséquence. S’il n’y a pas d’erreur, on se contente de retourner le composant children (le composant immédiatement déclaré après le composant ErrorBoundary)

cas #1

On modifie un chouya notre composant MyInput pour lever une erreur :

See the Pen JpzLjY by daibai (@Daibai) on CodePen.


On modifie maintenant notre container Login de façon à encapsuler notre form dans notre composant ErrorBoundary :

_renderForm = () => {
  return (
    <div>
      <ErrorBoundary>
        <Formsy onValidSubmit={this._handleSubmit} onValid={this.enableButton} onInvalid={this.disableButton}>
          <Header>Connexion</Header>
          <Form.Group widths='equal'>
 
            <MyInput
              name="email"
              validations="isEmail"
              label="Adresse E-Mail"
              placeholder="E-Mail"
              validationError="Adresse E-Mail non valide"
              required
              error={true}
            />
            <MyInput
              name="password"
              label="Mot de passe"
              placeholder="Mot de passe"
              validationError="Field empty"
              required
              type="password"
              error={false}
            />
 
          </Form.Group>
          <Form.Button disabled={!this.state.canSubmit}>Submit</Form.Button>
        </Formsy>
      </ErrorBoundary>
    </div>
   )
 }

Ici, on a un champ qui va provoquer une erreur. Dans la mesure où c’est le formulaire qui est contenu dans notre ErrorBoundary, c’est celui-ci et l’ensemble de ses composants enfants qui ne seront pas affichés :

cas #2

On ne touche pas à notre composant MyInput, par contre on va modifier un peu le code de notre Login :

<ErrorBoundary>
 <MyInput
 name="email"
 validations="isEmail"
 label="Adresse E-Mail"
 placeholder="E-Mail"
 validationError="Adresse E-Mail non valide"
 required
 error={true}
 />
</ErrorBoundary>

<ErrorBoundary>
 <MyInput
 name="password"
 label="Mot de passe"
 placeholder="Mot de passe"
 validationError="Field empty"
 required
 type="password"
 error={false}
 />
</ErrorBoundary>

On retire l’Error Boundary au niveau de Formsy et on encapsule nos deux inputs, dont l’un provoque toujours une erreur. On s’attend à ce que le premier champ affiche une erreur tandis que le second continue de s’afficher correctement.

Youpi !

Dans la vraie vie

Ici, on est bien d’accord, on a aucun intérêt à afficher le champ password si le champ login est pété. Dans la vraie vie, on se posera la question de placer nos Error Boundaries là où ils ont le plus d’intérêt pour les utilisateurs.

Par exemple, pour prendre un cas réel : si une zone de notre UI concerne l’affichage temps réel d’une course de poneys tandis que les autres parties de l’interface affichent des courses de licornes et des courses de tortues de mer, chacune de ces zones sera encapsulée dans un Error Boundary différent. Si la course de poney lève une erreur, on pourra continuer à voir l’évolution de notre licorne préférée dans la course.

Le code :

https://github.com/GregoryBabonaux/react-c-est-facile/tree/01c2529f2e9221193067fe722841dc6bbb4814d1

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *