Arquitetura Resiliente no React: Usando Error Boundaries
Tags: react, frontend, tutorial, beginners
Published: 2023-11-02 20:19:20

Recentemente estava implementando numa página um leitor de pdf para uma revista, testei no browser desktop e tudo funcionava perfeitamente.

O problema nasceu no mobile, pois após avançar algumas páginas do pdf o site quebrava completamente: “Application error: a client-side exception has occurred (see the browser console for more information).”.


![Console error img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/511kfx2s891hkiqejaqz.png)

## Resumo do conteúdo
<ul>
    <li>
        <a href="#errorboundaries">O que são Error Boundaries?</a>
    </li>
    <li>
        <a href="#quandousar">Quando usar e quando não usar</a>
    </li>
    <li>
        <a href="#Opcionais">Opcionais</a>
    </li>
    <li>
        <a href="#terceiros">Bibliotecas de Terceiros</a>
    </li>
</ul>

<b><b>


Não vou falar da solução, mas sim de como evitar que sua aplicação quebre completamente nesses casos, utilizando o conceito de Error Boundary para apresentar um UI alternativa em caso de erro.


<div style="display: flex; justify-content: space-between;">
<figure style="width: 48%; margin-right: 2%;"> 
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3t7n1qo9vmf2rhkhm3n.png" alt="Tela de erro nao tratada" />

  <figcaption>Tela de erro nao tratada</figcaption>
</figure>

<figure style="width: 48%;"> 
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9s46vqz2v6d94di7yxwz.png" alt="UI com tratamento de error boundary" />

  <figcaption>UI com tratamento de error boundary</figcaption>
</figure>
</div>


Antes do React 16, um erro em qualquer componente poderia levar a falhas em toda a aplicação, pois o erro era propagado (intensionalmente, nao é um bug, é uma feature mesmo🫣), quebrando completamente o site e criando uma experiência ruim para o usuário. Com a chegada do React 16, os Error Boundaries foram introduzidos, permitindo que desenvolvedores capturassem e tratassem erros em componentes específicos, sem derrubar toda a aplicação.

**O que são Error Boundaries?** <a id="errorboundaries" href="#errorboundaries">&#128279;</a>

Error Boundaries são componentes React que capturam erros JavaScript em seus componentes filhos, registram esses erros e exibem uma interface de fallback em vez do componente que falhou, funcionando como um *catch{} para componentes.*

Atualmente, é disponível somente como componente de classe e, até o momento, não há indícios da equipe do React querer mudar isso (ok, oficialmente nao tem, mas voce vai encontrar formas de fazer isso tanto manualmente quanto com libs externas e falarei disso mais pra frente.)

**Exemplo de código**:

```jsx
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    logErrorToService(error, info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Algo deu errado.</h1>;
    }
    return this.props.children;
  }
}

```

Vamos entender melhor o papel das funções:

- **`getDerivedStateFromError`**: Esta função estática é usada para atualizar o estado local do componente e renderizar uma UI de fallback após um erro.
- **`componentDidCatch`**: Este método é acionado após um erro ser capturado pelo Error Boundary. É útil para registro de erros ou efeitos colaterais.

Ao usar o Error Boundary em sua aplicação, envolva os componentes que você deseja proteger:

```jsx
<ErrorBoundary>
 <SeuComponente />
</ErrorBoundary>
```

Voce tambem pode envolver toda a aplicacao, tambem pode envolver o conteudo do site excluindo o layout ou ate mesmo somente rotas, tudo depende do design que voce escolher e que fizer mais sentido no seu contexto.


**Quando usar e quando não usar**  <a id="quandousar" href="#quandousar">&#128279;</a>

**Quando usar**:

- Em componentes ou funcionalidades não críticas.
- Quando incorporar widgets ou bibliotecas de terceiros.

**Quando não usar**:

- Para erros esperados (como erros de API conhecidos).
- Dentro de manipuladores de eventos.
- Em erros que devem ser tratados de maneira específica e não genérica.
- **Código assíncrono**: Error Boundaries não capturam erros em operações assíncronas fora de componentes React, como **`setTimeout`** ou **`requestAnimationFrame`**. Para tratar erros em operações assíncronas dentro de componentes, você deve usar blocos **`try/catch`**.
- Renderização do lado do servidor
- Erros lançados no próprio Error Boundary.

**Opcionais:** <a id="opcionais" href="#opcionais">&#128279;</a>

- Integre Error Boundaries a soluções de monitoramento de erros para rápida identificação e correção, voce pode integrar com Sentry ou Exceptionless por exemplo.
- Teste seus Error Boundaries para garantir que eles funcionem como esperado em diferentes cenários.

**Bibliotecas de Terceiros: `react-error-boundary`** <a id="terceiros" href="#opcionais">&#128279;</a>

Enquanto os Error Boundaries nativos do React são poderosos, algumas bibliotecas de terceiros foram desenvolvidas para simplificar e enriquecer essa funcionalidade. Uma das mais populares é a **`react-error-boundary`**.

**Principais benefícios**:

**API Simplificada**: Com **`react-error-boundary`**, você pode criar um boundary de erro com uma simples função de fallback, sem a necessidade de criar uma classe componente, alem de oferecer diversas outras possibilidades, que nao irei tratar hoje.

exemplo com **`react-error-boundary`:**

```jsx

import { ErrorBoundary } from 'react-error-boundary'

function FallbackComponent({ error }) {
  return <div>Ocorreu um erro: {error.message}</div>
}

<ErrorBoundary FallbackComponent={FallbackComponent}>
  <SeuComponente />
</ErrorBoundary>

```

**Hooks e Utilitários Adicionais**: A biblioteca oferece o hook **`useErrorHandler`**, que permite capturar e tratar erros dentro de componentes funcionais e funções personalizadas.

**Reinicialização Automática**: Com a propriedade **`onReset`**, você pode definir como o componente deve se reiniciar após um erro.