Alura > Cursos de Front-end > Cursos de React > Conteúdos de React > Primeiras aulas do curso React Vite: monitorando aplicações com New Relic

React Vite: monitorando aplicações com New Relic

Demostrando erros na aplicação - Apresentação

Olá, estudante da Alura! Meu nome é Pedro Mello, sou instrutor aqui na Alura, e quero te dar as boas-vindas a mais um curso de React, onde nosso principal foco será abordar temas de performance e observabilidade em aplicações React.

Audiodescrição: Pedro se descreve como um homem branco, de cabelo curto e castanho-escuro, barba castanho-escura, e olhos castanho-escuros. Ele tem piercings no nariz, veste uma camisa preta, e está sentado em um ambiente iluminado por luz azul-escura. Ao fundo, há instrumentos, quadros na parede, e um móvel com prateleiras e enfeites.

O que vamos aprender?

Para este curso, preparamos uma série de conteúdos interessantes. Vamos começar aprendendo como tratar erros em aplicações JavaScript. Portanto, vamos abordar ErrorBoundary e tratativas de erros em rotas que não existem na aplicação.

Também vamos construir nossa própria função de tratativa de erro, com um console de mensagens customizado, onde poderemos passar as mensagens e identificar a stack de onde vem o erro, para saber de qual componente, isto é, de qual local o erro origina.

Além disso, vamos conectar nossa aplicação a um serviço de observabilidade, para podermos, além do ambiente de desenvolvimento, ter acesso a essas informações em um ambiente produtivo, ou seja, nosso site estará disponível na web para acesso.

Não paramos por aqui: vamos aprender sobre as dashboards que esse serviço vai oferecer, entender quais são as métricas que elas vão retornar, e o quanto que elas podem ser úteis no nosso dia a dia.

Para fechar com chave de ouro, vamos aprender a criar uma GitHub Action para automatizar a geração de relatórios do Lighthouse quando formos fazer o merge (mesclagem) de alguma pull request na branch main do nosso projeto.

São muitos conteúdos interessantes para estudar neste curso!

Pré-requisitos

Para realizar este curso, será importante possuir um conhecimento prévio sobre React, TypeScript, funcionamento de hooks, e utilização do useContext() para gerenciamento de estado interno na aplicação. Além disso, abordaremos alguns tópicos de Tailwind e um pouco sobre o ciclo de vida de componente.

Conclusão

Esperamos você no próximo vídeo. Este curso tem muito a agregar! Você poderá utilizar esse conhecimento não só nos seus projetos pessoais, mas também nos projetos de empresa e projetos freelances que fizer.

Te aguardamos na nossa primeira aula. Até lá!

Demostrando erros na aplicação - Como tratar erros em uma aplicação React

Vamos começar a tratar os erros da nossa aplicação React?

Como tratar erros em uma aplicação React?

Na tela, temos a página da ByteBooks, um projeto provavelmente já conhecido por você. Caso contrário, ByteBooks é o projeto de uma editora de livros, onde temos filtragem de livros, uma listagem para os livros, uma página de detalhes, uma sacola e uma finalização de pedido.

O problema é que a nossa aplicação, no momento, não tem nenhuma tratativa de erro. Sendo assim, se ocorrer algum problema durante a requisição para buscar a listagem de livros, no fechamento de pedido, ou caso a pessoa usuária acesse uma rota que não está mapeada e não exista na nossa aplicação, não temos nenhuma blindagem de erros para isso.

Dito isso, o objetivo da nossa aula será entender quais são os principais pontos que podemos atacar para colocar essas tratativas de erro, e começar a desenvolver algumas estratégias no código para termos componentes que vão funcionar como fallback tanto no caso dos erros genéricos, que podem ser erros de requisição, quanto no caso da pessoa usuária acessar uma rota inexistente na aplicação.

Acessando o código

Vamos voltar ao código para entender como está o projeto no momento. Na página de catálogo, correspondente ao arquivo index.tsx dentro do diretório "pages > Catalog", na linha 23, assim que a aplicação é carregada, é feita uma requisição.

index.tsx ("src > pages > Catalog"):

useEffect(() => {
  fetchBooks();
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

fetchBooks() é uma função que vem dentro do hook useBooks(), que nada mais é do que o contexto referente a livros da nossa aplicação.

Podemos acessar a função useBooks() pela referência pressionando "Command", no caso no Mac, ou "Ctrl" no Windows, e clicando sobre ela no código. Seremos redirecionados para o arquivo books.tsx.

books.tsx:

const useBooks = () => {
  const context = useContext(BooksContext);
  if (!context) {
    throw new Error('useBooks must be used within a BooksProvider');
  }
  return context;
};

Se subirmos um pouco nesse arquivo, a partir da linha 64, encontramos toda a lógica de implementação da função fetchBooks(). Observe que a requisição que fazemos para o back-end, que retorna quais são os livros existentes, está dentro de um bloco de try…catch.

try {
  const response: Book[] = await fetch(
    'https://raw.githubusercontent.com/cicatriz-dev/bytebooks-assets/main/data.json'
  ).then((res) => res.json());

  await sleep(1500);

  setState({
    books: response,
    isLoading: false,
  });
} catch (error) {
  setState((prev) => ({
    ...prev,
    isLoading: false,
  }));
}

Tudo que está dentro do bloco try, ele tentará executar. Caso ocorra algum erro, ele vem para o bloco catch, que começa na linha 81. Por enquanto, ele recebe um error como parâmetro da função, mas esse error não é tratado. O máximo que ele faz é tirar o estado de loading (carregando) da tela.

Por exemplo: vamos passar uma URL inválida para a requisição na linha 72, salvar e voltar ao navegador. Ao recarregar, observamos que, no console de desenvolvimento, foram retornados alguns erros a nível de console de desenvolvedor, mas simplesmente foi removido o carregamento da tela.

Não foi informado para a pessoa usuária de uma forma amigável que houve um erro na requisição, que a URL é inválida, nenhuma informação do tipo. Esse é um dos tópicos que vamos abordar.

Outro ponto importante é sobre a navegação de rotas. Sabemos que uma das rotas existentes na nossa aplicação é a rota de livro. Se clicarmos em um livro na lista da página, vamos para uma rota específica (por exemplo: "localhost:5173/book/1"). Observe que, após o "localhost" e a porta da conexão, temos o "/book" e o ID do livro.

Se digitarmos no endereço da tela inicial ("localhost:5173"), sem nenhum parâmetro, o final "/alura" ("localhost:5173/alura"), será uma rota inexistente no nosso projeto.

Apesar disso, ele mostra um conteúdo de fundo branco, mas sem nada. Não é exibida para a pessoa usuária a informação de que essa é uma rota inválida. O que podemos fazer para tratar esse erro e começar a resolver alguns problemas do nosso projeto?

Solucionando rotas inválidas

No caminho "src/routes", vamos abrir o arquivo de rotas (index.tsx). Nesse arquivo, há um <Switch> na linha 12 que mostra as nossas rotas como se fosse uma ordem cronológica de acesso.

index.tsx ("src > routes"):

<Switch>
  <Route exact path='/' component={Catalog} />
  <Route path='/book' component={BookDetail} />
  <Route path='/order' component={Order} />
</Switch>

O /, correspondente à página inicial, fica em primeiro. Note que passamos exact como parâmetro de <Route>, para caso a pessoa usuária acessar / seguida de alguma coisa, não cair nessa rota. Portanto, o exact serve para abrir a página de catálogo (Catalog) apenas se for exatamente /. Depois temos as demais rotas do projeto: /book e /order.

Se quisermos implementar uma rota genérica para a nossa aplicação, ou seja, uma rota que servirá para qualquer acesso diferente das rotas que já existem, faremos o seguinte:

  1. Vamos passar uma nova <Route>;
  2. O path, que em vez de receber algum conteúdo, receberá um caractere coringa, que é o asterisco (*). Ou seja, qualquer rota diferente das três que já existem cairá nessa condição;
  3. Como component, colocaremos um texto (<h1>) apenas para verificar se será renderizado em tela.
<Switch>
  <Route exact path='/' component={Catalog} />
  <Route path='/book' component={BookDetail} />
  <Route path='/order' component={Order} />
  <Route path='*' component={() => <h1>Não ha nada aqui.</h1>} />
</Switch>

Após salvar o arquivo de rotas, vamos voltar ao navegador e atualizar a página. No canto superior esquerdo, abaixo do logo da ByteBooks, temos o texto "Não há nada aqui".

Conclusão

Agora que vimos em funcionamento o <h1> que definimos na nossa rota, aguardamos você no próximo vídeo para fazermos a implementação de um componente um pouco mais visual, que dará um feedback melhor para a pessoa usuária. Nos encontramos no próximo vídeo!

Demostrando erros na aplicação - Criando uma rota de erro

Neste vídeo, vamos criar nosso componente de forma mais elegante para a pessoa usuária. Afinal, apenas mostrar um texto "Não há nada aqui" sem um botão para retornar à página inicial, caso desejado, não é a melhor experiência possível para a pessoa usuária. Sendo assim, vamos voltar ao código.

Criando uma rota de erro

Dentro da pasta "pages", vamos criar um novo arquivo ("New File…") chamado NotFound/index.tsx. Dessa forma, é criada tanto a pasta "NotFound" quanto o arquivo index.tsx para inserir código.

Criando o componente NotFound

Vamos iniciar nosso componente como uma const chamada NotFound, que receberá uma arrow function. No escopo dessa função, primeiramente, vamos importar nossa navegação. Para isso, digitamos const navigation que receberá useHistory().

index.tsx ("src > pages > NotFound"):

import { useHistory } from 'react-router-dom';

const NotFound = () => {
  const navigation = useHistory();
}

Essa função será utilizada para redirecionar a pessoa usuária quando ela clicar no botão. Então, ao clicar no botão que colocaremos na página, a pessoa será redirecionada para a página de catálogo, que é a página inicial do projeto.

Adicionando o retorno

Em seguida, vamos adicionar um return e, entre parênteses, abrir uma <div> que englobará nosso componente nesta página. Vamos adicionar como className o valor flex1, para preencher todo o espaço disponível na tela. Também definiremos que todo o conteúdo será flex e estará dentro de um flex col, ou seja, estará organizado em colunas.

Além disso, centralizaremos todos os itens (items-center) e justificaremos eles ao centro da tela (justify-center). Por fim, como já temos o header da aplicação, com a logo da ByteBooks e a sacola, vamos dar uma margem de 16 pixels (mt-4) para um espaçamento adequado entre os conteúdos.

// código omitido

const NotFound = () => {
  const navigation = useHistory();
  
  return (
    <div className="flex-1 flex flex-col items-center justify-center mt-4">
    
    </div>
  )
}

Adicionando um texto

Com a <div> fechada, vamos adicionar um texto em <h2>: "Oops! Não encontramos nada por aqui.". Dentro desse <h2>, vamos passar um className para que o texto receba algumas propriedades.

Esse texto será centralizado (text-center), utilizará uma cor específica (text-[#002F52]), e terá um tamanho de 32 pixels (text-[32px]).

<div className="flex-1 flex flex-col items-center justify-center mt-4">
  <h2 className='text-center text-[#002F52] text-[32px]'>
    Oops! Não encontramos nada por aqui.
  </h2>
</div>

Adicionando uma imagem

Adicionaremos também uma imagem ao nosso projeto, a qual já temos e usamos na página de catálogo. Faremos isso logo abaixo de <h2>, adicionando a tag <img>.

Lembre-se de que as nossas imagens estão na pasta "public", então, ao gerar o estático do site para o server-side rendering ou para uma aplicação rodando apenas do lado do cliente, não precisamos fazer o caminho completo para importá-la. Podemos fazer o caminho relativo passando apenas a barra e o nome da imagem mais o formato dela.

Dito isso, o atributo src será /not_found.webp, que é o formato da imagem. Vamos passar uma mensagem alternativa (alt) caso a imagem não carregue, ou seja usado um navegador que não suporte carregamento de imagem, o que é bastante raro hoje em dia.

Por fim, passaremos algumas propriedades de estilização para className: passamos a largura (width) como 50% (w-1/2); a largura máxima (max-width) de 500px (max-w-[500px]); margem automática no eixo X (mx-auto), portanto, alinhada ao centro; e uma margem ao topo de 16px (mt-4).

<img src='/not_found.webp' alt='sem resultado' className='w-1/2 max-w-[500px] mx-auto mt-4' />

Adicionando um botão

Para finalizar, teremos um botão com algumas classes e um evento de clique.

<button
  className='py-3 w-1/4 bg-[#EB9B00] hover:opacity-80 rounded-md shadow-md'
  onClick={() => {
    navigation.push('/');
  }}
>
  <h3 className='text-white text-lg font-medium'>Voltar ao Inicio</h3>
</button>

Nesse botão, teremos um espaçamento interno (py-3), uma largura de 25% em relação à tela (w-1/4), um fundo laranja (bg-[#EB9B00]), uma opacidade ao passar o cursor (hover:opacity-80), um arredondamento das bordas (rounded-md), e uma sombra para destacar (shadow-md).

Além disso, há o evento de clique (onClick), que usará o navigation, importado na linha 4, para fazer a navegação até a página /, ou seja, a página inicial do projeto.

Dentro desse botão, também adicionamos um texto <h3> que foi "Voltar ao início". Passaremos para a tag <h3> o atributo className recebendo text-white, text-lg e font-medium.

No final, podemos fazer um export default NotFound.

Código final do arquivo index.tsx ("src > pages > NotFound"):

import { useHistory } from 'react-router-dom';

const NotFound = () => {
  const navigation = useHistory();

  return (
    <div className='flex-1 flex flex-col items-center justify-center mt-4'>
      <h2 className='text-center text-[#002F52] text-[32px]'>
        Oops! Não encontramos nada por aqui.
      </h2>
      <img src='/not_found.webp' alt='sem resultado' className='w-1/2 max-w-[500px] mx-auto mt-4' />
      <button
        className='py-3 w-1/4 bg-[#EB9B00] hover:opacity-80 rounded-md shadow-md'
        onClick={() => {
          navigation.push('/');
        }}
      >
        <h3 className='text-white text-lg font-medium'>Voltar ao Inicio</h3>
      </button>
    </div>
  );
};

export default NotFound;

Adicionando o novo componente na tela de rotas

Feito isso, podemos salvar o arquivo, e na tela de rotas ("src > routes > index.tsx"), na linha 16, em vez de utilizar <h1>, vamos trocar pelo novo componente que criamos.

index.tsx ("src > routes"):

<Route path='*' component={NotFound} />

Nessa etapa, deve ser feita também a importação do NotFound na linha 8:

import NotFound from '../pages/NotFound';

De volta ao nosso navegador, ele já mostra a tela de tratamento de rotas, com a mensagem que definimos, a imagem, que é a mesma utilizada no catálogo caso não exista nenhum resultado buscado, e o botão que, ao ser clicado, retorna à tela inicial que é a de catálogo.

Qualquer rota que digitarmos após isso, se ela não existir, seremos redirecionados para a tela que acabamos de criar. Reforçando: de volta ao código, o caractere coringa fará toda a mágica para nós; se nenhuma das rotas definidas for acessada, vamos para a do componente NotFound.

Conclusão

A ideia é utilizar esse componente como um fallback justamente para otimizar a experiência da pessoa usuária na nossa aplicação. Não queremos que a pessoa usuária acesse uma rota inexistente ou tenha qualquer tipo de erro relacionado à rota e fique sem uma informação sobre isso.

Dessa forma, tratamos os problemas de rotas inexistentes na aplicação. Agora vamos criar um componente chamado ErrorBoundary para tratar diversos outros tipos de erro na nossa aplicação, como erros sobre requisição, por exemplo. Abordaremos isso e muito mais no próximo vídeo. Te esperamos lá!

Sobre o curso React Vite: monitorando aplicações com New Relic

O curso React Vite: monitorando aplicações com New Relic possui 120 minutos de vídeos, em um total de 39 atividades. Gostou? Conheça nossos outros cursos de React em Front-end, ou leia nossos artigos de Front-end.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda React acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas