Evite que seu React quebre!

Leona Souza
Gamers Club - Escuro Baixo (de 🏡)
4 min readSep 13, 2023

Sabe aquela situação em que sua aplicação em React simplesmente desaparece do nada e, quando você vai debugar, descobre que o JavaScript estava tentando acessar uma propriedade indefinida? Seus problemas acabaram! Hoje vou comentar como implementamos o tratamento de erros no front-end da Gamers Club e, de bônus, dicas de como implementamos o Sentry para monitoramento de front-end.

Problema: “cannot read property of undefined”

Mesmo usando TypeScript, às vezes os objetos que utilizamos podem ser alterados de forma inesperada. Por exemplo, quando um endpoint não retorna um dado que o front estava esperando. Nesses casos o JavaScript produz um erro e o React não sabe muito bem como reagir. Vamos usar como exemplo a seguinte estrutura:

<Container>
<List>
<Produto id={1} />
</List>
</Container>

Vamos supor que, dentro do componente Produto, o endpoint que retorna os dados do produto não está funcionando como o esperado. O React tenta ler produto.nome (ainda dentro de Produto), mas não há nome no objeto. O erro acontece em Produto, mas o React procura algum tratamento fora do componente, no nosso caso em List. Mas também não há tratamento no List, então o React procura algum tratamento em Container. Chegamos no último componente possível e não há tratamento para o erro, então o React simplesmente vai tomar um cafezinho e desaparece.

Solução: ErrorBoundary

Segundo a própria documentação do React, podemos tratar os erros usando um componente chamado ErrorBoundary (veja a estrutura do componente clicando aqui). Esse componente deve ser um componente de classe (até o momento que esse artigo foi escrito) e deve envelopar componentes suscetíveis a erro. Veja a seguir como ficaria nosso componente de produto:

<Container>
<List>
<ErrorBoundary fallback={<ErrorFallback onError={() => cry()} />}>
<Produto id={1} />
</ErrorBoundary>
</List>
</Container>

Agora, quando o erro acontecer em Produto, o React vai encontrar um tratamento no componente pai (ErrorBoundary). O componente pai vai parar o erro por ali e, ao invés de sumir, vai exibir o que passamos como fallback, que pode ser outro componente JSX, no lugar do componente filho. Na Gamers Club criei um componente chamado ErrorFallback, onde customizamos visualmente a mensagem de erro, callbacks e outras coisas.

Um exemplo prático da utilidade dos componentes é que temos vários produtos que ficam à mostra na loja da GC e, quando algum deles dava problema, a loja inteira sumia. Agora, se houver erro, apenas o produto com erro será substituído por um componente visual de erro e o restante da loja continuará funcionando normalmente.

Como saber que há erros?

Uma das melhores partes de usar ErrorBoundary é usar o método interno chamado componentDidCatch() que pode executar funções quando houver um erro capturado. Ou seja, podemos registrar logs via Sentry, que é uma das ferramentas mais populares para monitoramento de erros de aplicações. Instalei a biblioteca @sentry/react e fiz assim:

public componentDidCatch(error: Error, info: ErrorInfo) {
Sentry.withScope((scope) => {
scope.setExtra('componentStack', info) // informação opcional
scope.setExtra('userId', userId) // informação opcional
Sentry.captureException(error)
})

this.props.onError && this.props.onError()
}

Pronto! Agora é só ficar de olho no dashboard do Sentry e monitorar o projeto. Você pode ficar sabendo quando algum erro acontece antes mesmo que haja uma reclamação e ainda terá informações suficientes para debugar.

Dicas de configurações para React + Sentry

Dependendo do tamanho do seu projeto pode ser que você receba um número muito alto de erros de bibliotecas externas ou erros que na verdade não são erros. Muitos desses erros podem ser ignorados e só poluem o histórico no dashboard. Podemos contornar esse problema de algumas formas no arquivo de configuração (caso não saiba configurar, veja aqui).

defaultIntegrations

Sentry.init({
dsn: SENTRY_DSN,
defaultIntegrations: false
})

O defaultIntegrations como false vai impedir registros automáticos do Sentry. Neste caso os únicos registros serão os chamados “na mão” com o Sentry.captureException(error) citado anteriormente no componentDidCatch().

sampleRate

Sentry.init({
dsn: SENTRY_DSN,
sampleRate: 0.1 // equivalente a 10%
})

O sampleRate vai limitar a quantidade de registros a uma porcentagem passada como valor. No exemplo acima só teremos registros de 10% dos erros. Isso é muito útil em projetos grandes, onde apenas uma pequena amostra pode ser suficiente.

ignoreErrors

const ignoreErrors = [
/pedaço da mensagem de erro/i,
"Cannot read properties of null (reading 'nome')"
]

Sentry.init({
dsn: SENTRY_DSN,
ignoreErrors
})

O ignoreErrors vai ignorar um array de erros que possuam a mensagem igual (no caso de strings) ou parcial (via regex). Mas atenção, pois essa configuração pode não funcionar caso a opção defaultIntegrations esteja como false. A razão disso é que uma das integrations do Sentry é responsável justamente por essa filtragem (saiba mais sobre integrations clicando aqui). Caso você queira desabilitar todas as integrations, como citei anteriormente, exceto a responsável por essa filtragem, faça uma combinação como essa:

const ignoreErrors = [
/pedaço da mensagem de erro/i,
"Cannot read properties of null (reading 'nome')"
]

Sentry.init({
dsn: SENTRY_DSN,
defaultIntegrations: false,
integrations: [new Sentry.Integrations.InboundFilters()],
ignoreErrors
})

beforeSend()

Por fim, podemos usar o método beforeSend() para customizar o erro antes de ser enviado e até mesmo ignorá-lo (leia mais clicando aqui).

const environment = process.env.NODE_ENV

Sentry.init({
dsn: SENTRY_DSN,
beforeSend: (event) => {
if (environment === 'production') {
return event
}

return null
},
})

Referência e leitura adicional

https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary
https://docs.sentry.io/platforms/javascript/guides/react/

--

--