User Feedback — Error Handling

João Almeida
MCTW_TDW
Published in
6 min readMar 13, 2022

Nas plataformas web é pretendido que o cliente adore a sua experiência de utilização da plataforma, que volte a navegar nela e, caso seja um loja online faça muitas compras. No entanto, atualmente no desenvolvimento destas plataformas é necessário estar muito atento ao utilizador, como melhorar a sua experiência, que funcionalidades este gostaria de ter ao seu dispor, etc. Em consequência, são feitas muitas passagens de código para produção e consequentemente, embora sejam feitos testes e seguidas outras práticas de qualidade de código, é frequente escaparem alguns erros para a camada de produção. Assim, surge a necessidade de ter mecanismos que permitam informar a equipa de desenvolvimento quando ocorre um erro na navegação do utilizador. Além disso, é preciso muito cuidado na forma como são geridos os erros que ocorrem na navegação do utilizador para que eles não fiquem perdidos sem saber o que fazer e deixem de usar a plataforma. Este artigo explora soluções para estas temáticas na perspetiva do React, uma bilbioteca de JavaScript.

React

Quando acontece um erro o utilizador fica bloqueado. Podemos tentar minimizar os erros, mas é inevitável que eles aconteçam. A melhor solução é oferecer explicações dos erros o mais específicas possíveis. Além disso, estas explicações devem ser claras e fáceis de compreender e, se for precisa alguma ação do utilizador esta deve ser descrita explicitamente.

Em aplicações/websites/projetos/produtos de média ou grande dimensão é insustentável validar individualmente cada componente para saber qual foi o erro que aconteceu e que mensagem se deve mostrar ao utilizador. Além disso, muitas destas verificações seriam repetidas devido a validarem o mesmo tipo de erros (embora hajam vários tipos de erros diferentes). Felizmente, em React existem alternativas como, por exemplo, higher-order component (HOC).

Higher-order component (HOC)

Higher-order component (HOC) são componentes que adicionam funcionalidades a outros componentes. Imagina, por exemplo, que queres saber em vários componentes qual é o innerWidth da window sem estar a repetir sempre a mesma lógica. Para isso, podes fazer com:

import {useState, useEffect} from “react”;const withResize = Component => props => {     const [innerWidth, setInnerWidth] = useState(window.innerWidth)     const handleResize = () => {          setInnerWidth(window.innerWidth)     }     useEffect(() => {          window.addEventListener(‘resize’, handleResize)          return () => { // componentWillUnmount               window.removeEventListener(‘resize’, handleResize)          }     }, []) // componentDidMount     return <Component {…props} innerWidth={innerWidth} />}export default withResize;

Como podes ver, os HOC fazem “spread” dos props que recebem do componente porque tendem a ser transparentes e apenas adicionar funcionalidades.

Por convenção, os HOC têm o prefixo “with” (exemplo: withResize) e o nome do ficheiro sem o prefixo (exemplo: Resize.js) numa pasta hocs/.

Podemos aplicar esta estratégia no contexto de dar feedback ao utilizador. Por exemplo, pode-se desenvolver-se um HOC que:

  • conforme o tipo de conteúdo que está a carregar mostra um loader diferente;
  • quando o pedido de informação à API retorna um erro ter um HOC que verifica com base no código do erro qual é a mensagem de feedback de erro que deve ser mostrada ao utilizador;
  • quando não é obtida nenhuma informação por ainda não existir na base de dados associada à API, por exemplo, uma lista de livros que ainda não tem livros, mostra uma mensagem de feedback que explique ao utilizador que ainda não foi adicionado nenhum livro à lista que está a ver;
  • valida se a informação recebida da API é a que é espectável; por exemplo, se está à espera de receber uma listagem de livros em array, se tal não estiver a acontecer é preciso informar a equipa de desenvolvimento que está a ocorrer um erro.

Este tipo de funcionalidade, isto é, desenvolver uma vez e utilizar noutros componentes, também é possível através de Hooks e Context. Em React, devido à sua natureza declarativa não se usam mixins.

Error Boundary

Outro mecanismo que atualmente existe para validar qual foi o erro que ocorreu e mostrar ao utilizador uma mensagem de feedback de error apropriada são os Error Boundarys. Estes são componentes de classes que apanham os erros que ocorrem nos seus componentes filhos prevenindo erros críticos que sem eles a interface do utilizador ficaria em branco, sem possibilidade de informar o utilizador sobre o que se passou. Além disso, permitem fazer o seu log, isto é, permite alertar a equipa de desenvolvimento para o erro que aconteceu, e mostrar um UI (User Interface) preparado para quando ocorre um erro.

Para um componente de classes ser um Error Boundary apenas é preciso definir pelo menos um dos métodos de lifecycle: static getDerivedStateFromError() ou componentDidCatch(). Quando ocorre um errro, o static getDerivedStateFromError() renderiza um UI com uma mensagem de feedback predefinida ao utilizador. Por outro lado, o componentDidCatch() permite fazer o log do erro.

class ErrorBoundary extends React.Component {     constructor(props) {          super(props);          this.state = { hasError: false };     }     static getDerivedStateFromError(error) {     // Update state so the next render will show the fallback UI.          return { hasError: true };     }     componentDidCatch(error, errorInfo) {     // You can also log the error to an error reporting service          logErrorToMyService(error, errorInfo);     }     render() {          if (this.state.hasError) {          // You can render any custom fallback UI          return <h1>Something went wrong.</h1>;          }     return this.props.children;     }}

No entanto, os Error Boundarys não conseguem apanhar erros de:

  • Event handlers: caso ocorram o React sabe que UI mostrar; para gerir erros neste caso é utilizado try catch;
  • Asynchronous code (como setTimeout);
  • Server side rendering;
  • Erros lançados no próprio Error Boundary;

Numa aplicação deve haver pelo menos um Error Boundary no App.js (no início da React tree) para certificar que o utilizador nunca fica perdido com ecrã branco ou cheio de erros. Adicionar mais Error Boundarys depende do website ou aplicação. Podes querer mostrar, para além de uma página de erro geral costumizada, outras diferentes em contextos mais específicos.

Quando um erro acontece no Error Boundary pode-se despoltar um log do erro. Mas, como podemos fazer isso? Devemos desenvolver o código ou utilizar um serviço externo?

Dependendo da aplicação/website podes estar a usar algo para gerir o seu estado, como por exemplo o Redux, uma biblioteca de Javascript muito popular atualmente. Esta consiste numa caixa (container) de estado global da aplicação que pode ser acedida em qualquer parte da aplicação. Por exemplo, se dois componentes separados na react tree precisarem da mesma informação da API, pode-se fazer o pedido pelo Redux à API e, assim, esses dois componentes podem ir buscar a informação ao mesmo estado global da aplicação. Neste caso, podes aproveitar a lógica do Redux através de Redux middleware para reportar os erros (loggers, etc).

Além disso, com o Redux também é posssível desenvolver middleware para monitorizar o desempenho da aplicação (enhancers). Estes dão a vantagem de permitirem acompanhar o desempenho em tempo real da aplicação. E, assim, é possível obter informação muito valiosa sobre as condições em que está a ser usada a aplicação e os tempos de processamento no lado do cliente, para além da necessidade de infraestrutura do lado do servidor (backend).

No entanto, pode-se escolher não desenvolver esta tecnologia, por exemplo por falta de tempo e, por isso, recorrer-se a um serviço externo. Por um lado, estes serviços externos têm a grande vantagem de já estarem otimizados. Por outro lado, estes requerem menos manutenção por parte da equipa de desenvolvimento, além de oferecerem outras funcionalidades. Por exemplo, existe o Sentry. Este é um serviço que regista quando ocorrem erros e monitoriza o desempenho da aplicação. Além disso, está preparado tanto para o frontend como para backend.

Conclusão

Com este artigo pudeste compreender a importância de gerir os erros e mostrar o feedback correto ao utilizador e como poderias implementar esta tecnologia para solucionar este problema. Além disso, agora sabes a importância da equipa de desenvolvimento ser alertada em tempo real sobre erros que ocorram com o utilizador e de que forma se pode implementar, seja pela equipa ou através de um serviço externo.

--

--