Trabalhando com Rotas

Trabalhando com Rotas (React-Router-Dom)

Olá leitor, seja muito bem vindo a mais uma lição de ReactJS!

Hoje nós aprenderemos a dar a vida (de forma definitiva) em nossas aplicações criadas com o ReactJS, em conjunto com a famosa biblioteca React-Router-Dom.

Se você acompanhou essa jornada desde o início, notou que até agora nós estamos criando aplicações para web que... NÃO GERENCIAM NOSSAS URLs 🤯

E crriar aplicações capazes de fazer esses gerenciamentos, é um grande passo, pois você já sabe como criar seus componentes, já entende como criar suas páginas, mas... não sabe como criar links entre elas 😶

Mas nesta lição, você vai aprender a gerenciar as URLs da sua aplicação, de modo que quando o seu usuário clique em dashboard, a aplicação abra a página /dashboard, quando ele clicar em configurações, a sua aplicação abra a página/configuracoes e assim por diante!

Criando seu projeto de testes

Antes de começarmos, vamos criar um novo projeto em ReactJS dedicado a esta lição. No meu caso criei um novo projeto chamado de app-rotas:  

npx create-react-app app-rotas

Após isso, não se esqueça de fazer aquela limpeza do código, e também aquela organizada nas pastas do src (organização por funcionalidade) 😁

Feito isso, vamos começar a criar nossas primeiras páginas!

Criando nossas primeiras páginas

Para um site ser chamado de site, ele deve possuir no mínimo de 2 ou mais páginas, não é verdade?

Porque se não ele não se chamaria de site, e sim de Landing Page 😜

Sendo assim, partiu criar nossas 3 páginas dentro da nossa aplicação em ReactJS, que são:

  • Home
  • Contato
  • Servicos

Não se esqueça de que cada uma dessas páginas devem ser criadas dentro de src > pages.

Home > index.jsx:

const Home = () => {
 return (
 <div>
 <h1>Página Home</h1>
 <p>Seja muito bem vindo a página principal do nosso site =)</p>
 </div>
 );
}

export default Home;

Contato > index.jsx:

const Contato = () => {
 return (
 <div>
 <h1>Página Contato</h1>
 <p>Em caso de dúvidas entre em contato conosco pelo nosso Whatsapp: (99) 99999-9999.</p>
 </div>
 );
}

export default Contato;

Servicos > index.jsx:

const Servicos = () => {
 return (
 <div>
 <h1>Página Serviços</h1>
 <p>Trabalhamos com desenvolvimento de sites, estratégias de marketing e nas horas vagas vendemos armas de airsoft.</p>
 </div>
 );
}

export default Servicos;

Como podemos notar acima, nós temos 3 páginas bem simples que mostram informações diferentes.

Para utilizar o sistema de roteamento em nossas aplicações,nós vamos precisar instalar uma nova biblioteca chamada de React Router DOM 😄

O que é o React Router DOM?

O React Router DOM nada mais é do que um pacote npm que podemos instalar na nossa aplicação feita com ReactJS, de modo a implementar um roteamento dinâmico voltado para aplicativos web.

Basicamente, ele nos permite gerenciar e exibir páginas de acordo com a URL que está sendo aberta no navegador.

Desse modo, por de baixo dos panos, existe um código Javascript que é capaz de interpretar a URL atual, exibindo a página em que ela está ligada, tal processo é conhecido como roteamento.

O React Router DOM é usado para construir aplicativos de página única, ou seja, aplicativos que possuem muitas páginas ou componentes diferentes.

Uma das suas principais vantagens, é que essa biblioteca não precisa atualizar a aplicação (fazer refresh) sempre quando um link é aberto.

Não só isso, como a biblioteca tem uma velocidade de carregamento de páginas muito superior se comparado ao método antigo de navegação entre páginas (React Router).

O que melhora significativamente a experiência do usuário, sendo a melhor escolha para um melhor desempenho da sua aplicação no geral.

Instalando a biblioteca do React Router DOM

Para instalar a biblioteca, abra o terminal (prompt de comando) dentro da pasta raiz da sua aplicação, e execute:

npm install react-router-dom

Após a instalação, execute novamente o seu projeto (npm start), e vamos aprender a criar nossas primeiras rotas 😆

Criando rotas dentro da aplicação

Você se lembra daquela pastinha chamada routes que existe dentro de src, que criamos em algumas lições anteriores?

Então... chegou a hora de usá-la:

Dentro dessa pasta nós iremos criar um arquivo bem especial chamado de routes.js, que por sua vez, vai conter todas as configurações de rotas que estão atreladas às páginas em que a nossa aplicação vai exibir.

Sendo assim, crie um novo arquivo chamado de routes.js dentro de src > routes:

import { Routes, Route } from 'react-router-dom';

import Home from '../pages/Home';
import Contato from '../pages/Contato';
import Servicos from '../pages/Servicos';

function RouterApp(){
 return(
 <Routes>
 <Route path="/" element={ <Home /> } />
 <Route path="/servicos" element={ <Servicos /> } />
 <Route path="/contato" element={ <Contato /> } />
 </Routes>
 );
}

export default RouterApp;

Agora vamos às explicações...

Para que o roteamento possa funcionar na nossa aplicação, nós precisamos importar dois componentes da biblioteca React Router DOM chamados de:

Routes: ele é um componente de nível superior que você utiliza para definir as rotas da sua aplicação, sendo responsável por mapear URLs específicas para componentes em ReactJS, que devem ser renderizados quando essas URLs o corresponderem.

Route: é um componente que você usa dentro do Routes para definir como e quando determinado componente em ReactJS deve ser renderizado com base na URL atual.

É importante ressaltar que o componente <Route /> faz o uso de dois atributos como: path e element.

O path é uma propriedade do componente Route que define o padrão de URL que esse Route deve corresponder. Em outras palavras, ele especifica qual URL ativa deve acionar o rendering do componente especificado pelo element.

Sendo assim, quando queremos criar uma nova URL, precisamos definir um componente <Route /> em conjunto com o caminho especificado dentro do path, e o componente (página) que será executado quando o usuário abrir tal URL.

Por exemplo, vamos supor que nós queremos criar duas novas URLs chamadas de /configuracoes e /perfil, para isso poderiamos criar dois componentes <Route /> da seguinte forma:

<Route path="/configuracoes" element={ <Configuracoes /> } />
<Route path="/perfil" element={ <Perfil /> } />

E é claro, sem se esquecer de importar às páginas de Configuracoes e Perfil para dentro do routes.js.

Também é possível criar sub-URLs da seguinte forma:

<Route path="/perfil/mudar-senha" element={ <MudarSenha /> } />
<Route path="/produto/maquina-de-lavar" element={ <MaquinaDeLavar /> } />

Perfeito, agora que nós já temos o arquivo routes.js configurado, vamos implementá-lo dentro do App.js 🙂

Pois sem essa última configuração, às nossas rotas não irão funcionar.

Importanto nossas rotas na nossa aplicação principal

Para que a biblioteca funcione corretamente, nós vamos precisar importar o nosso arquivo de rotas (routes.js) dentro do App.js da seguinte forma:

import { BrowserRouter } from 'react-router-dom';
import RouterApp from './routes/routes.js';

export default function App() {
 return (
 <BrowserRouter>
 <RouterApp /> 
 </BrowserRouter>
 );
}

O BrowserRouter é um componente fornecido pela biblioteca react-router-dom, que é usada para gerenciar a navegação em uma aplicação ReactJS que utiliza roteamento.

Ele é o pai dos outros componentes que inserimos dentro do arquivo routes.js (Routes e Route), sem ele, o processo de roteamento de uma aplicação em ReactJS não funciona.

O BrowserRouter deve englobar o RouterApp que nada mais é do que o componente que criamos em routes.js, e que representa nosso arquivo que contém todas as configurações de rotas.

Só com essas configurações, nós já somos capazes de navegar na nossa aplicação:

Legal, mas nosso usuário não vai ficar digitando a URL de cada uma das páginas para ficar mudando de tela de forma manual, não é verdade?

É nesse momento que precisamos do componente <Link />.

Componente Link

No ReactJS nós costumamos representar a tag <a> como <Link />.

O <Link /> nada mais é do que um componente usado para criar links de navegação dentro de uma aplicação web baseada em ReactJS.

Ele é capaz de criar uma navegação entre telas sem a necessidade de carregamento, proporcionando uma experiência mais suave e rápida.

Ao contrário dos <a> tags tradicionais, que causam um recarregamento completo da página, <Link /> usa o roteador interno do ReactJS para manipular a navegação, evitando esse comportamento indesejado.

Mas isso não quer dizer que você não pode usar uma tag <a> para levar seus usuários de uma página para outra, você pode sim, mas o comportamento de um <Link /> é melhor se comparado a tag <a>.

Para utilizá-lo na sua aplicação, você precisa importá-lo dentro das suas páginas ou componentes que possuem links:

import { Link } from 'react-router-dom'

Em seguida basta declarar o componente <Link /> dentro do return do JSX da seguinte forma:

return(
 <div>
 <Link to="/dashboard">Dashboard</Link>
 </div>
)

O atributo to funciona de forma bastante similar ao href de um <a> normal, cujo o objetivo é levar o usuário a URL especificada.

Caso desejar, você também pode estilizar o componente <Link /> definindo um className ou style:

<Link to="/home" className="my-link" style={{ color: 'blue' }}>Abrir Página Inicial</Link>

É importante ressaltar que o Link só funciona quando ele está dentro da tag <BrowserRouter>.

Agora vamos atualizar nossas páginas de modo que nosso usuário consiga navegar por meio de links.

Home > index.jsx:

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

const Home = () => {
 return (
 <div>
 <h1>Página Home</h1>
 <p>Seja muito bem vindo a página principal do nosso site =)</p>
 <Link to="/contato">Entre em Contato</Link><br />
 <Link to="/servicos">Nossos Serviços</Link>
 </div>
 );
}

export default Home;

Contato > index.jsx:

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

const Contato = () => {
 return (
 <div>
 <h1>Página Contato</h1>
 <p>Em caso de dúvidas entre em contato conosco pelo nosso Whatsapp: (99) 99999-9999.</p>
 <Link to="/">Página Inicial</Link><br />
 <Link to="/servicos">Nossos Serviços</Link>
 </div>
 );
}

export default Contato;

Servicos > index.jsx:

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

const Servicos = () => {
 return (
 <div>
 <h1>Página Serviços</h1>
 <p>Trabalhamos com desenvolvimento de sites, estratégias de marketing e nas horas vagas vendemos armas de airsoft.</p>
 <Link to="/">Página Inicial</Link><br />
 <Link to="/contato">Contato</Link>
 </div>
 );
}

export default Servicos;

Veja como ficou o resultado final:

Criando uma rota 404 not found

Show, nós já sabemos como criar as nossas rotas, mas... o que acontece se o usuário digitar no navegador uma rota que não existe?

Nossa aplicação simplesmente abre uma página vazia, ou seja, nada é carregado 🥲

Apesar de que em uma aplicação já "buildada" (npm run build) e "deployada" (executando dentro de um servidor), o retorno seria um erro de 404 not found que seria gerado pelo próprio servidor em questão:

É claro que isso não acontece quando estamos executando nossa aplicação em um ambiente de desenvolvimento (npm start), em vez disso, uma página em branco é retornada.

Para resolver este problema, nós podemos criar uma página chamada de Error404 dentro de src > pages:

Error404 > index.jsx:

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

const Error404 = () => {
 return (
 <div>
 <h1>Error 404</h1>
 <p>Page not found</p>
 <Link to="/">Voltar para a Home</Link>
 </div>
 );
}

export default Error404;

Para que o React Router DOM chame essa página sempre quando um usuário tentar acessar uma URL inexistente, temos que alterar o nosso arquivo routes.js da seguinte forma:

import { Routes, Route } from 'react-router-dom';

import Home from '../pages/Home';
import Contato from '../pages/Contato';
import Servicos from '../pages/Servicos';
import Error404 from '../pages/Error404';

function RouterApp(){
 return(
 <Routes>
 <Route path="/" element={ <Home /> } />
 <Route path="/servicos" element={ <Servicos /> } />
 <Route path="/contato" element={ <Contato /> } />
 <Route path="*" element={ <Error404 /> } />
 </Routes>
 );
}

export default RouterApp;

Observe que o uso do asterísco (*) no path, já é mais do que o suficiente para redirecionar o nosso usuário para a página <Error404>:

<Route path="*" element={ <Error404 /> } />

Recebendo parâmetros nas rotas

O que seria do React Router DOM sem a capacidade de receber e enviar parâmetros nas rotas, nao é verdade?

Com a biblioteca, nós podemos passar alguns parâmetros e recebê-los via método GET.

Para atingir este objetivo podemos definir uma rota com um path capaz de receber parâmetros.

O que é bem útil quando você precisa definir uma rota dinâmica que depende de uma informação para carregar determinados dados. Note como isso pode ser feito:

<Route path="/produto/:id" element={<DetalhesDoProduto />} />

No caso do comando acima estamos definindo que a rota produto recebe um parâmetro chamado :id

Para capturar este :id, basta que dentro do seu componente (DetalhesDoProduto) você importe a função useParams da seguinte forma:

import React from 'react';
import { useParams } from 'react-router-dom';

const DetalhesDoProduto = () => {
 const { id } = useParams();

 return <div>Detalhes do produto {id}</div>;
};

export default DetalhesDoProduto;

Observe que estamos importando o useParams de 'react-router-dom', e dentro do componente nós estamos o recebendo dentro da constante id:

const { id } = useParams();

Para chamar essa rota basta usar o componente <Link> de modo a passar o id dentro do to:

<Link to="/produto/99">Acessar Produto 99</Link>

Veja como ficou o resultado final:

Também é possível definir diversos outros parâmetros dentro das nossas rotas, observe:

<Route path="/produto/:id/:desc" element={<DetalhesDoProduto />} />

<Route path="/produto/:id/:desc/:abc" element={<DetalhesDoProduto />} />

Não se esquecendo também, de recuperar cada um deles dentro do seu componente:

const { id, desc, abc } = useParams();

Legal, mas e se uma rota trabalha com parâmetros opcionais? Como isso funciona de fato?

Para isso basta envolver os parâmetros usando parêntesis da seguinte forma:

<Route path="/produto/:id/:desc(/:abc)" element={<DetalhesDoProduto />} />

O uso de parênteses () em torno de /:abc indica ao React Router DOM que essa parte da URL é opcional. Isso significa que a rota corresponderá tanto a URLs que incluem /:abc quanto a URLs que não incluem /:abc.

Uma outra forma de se definir parâmetros opcionais (usando a versão 5 da biblioteca em diante) é definir um ponto de interrogação nos parâmetros que são opcionais, da seguinte forma:

<Route path="/produto/:id/:desc/:abc?" element={<DetalhesDoProduto />} />

Nesta definição, o :id e :desc são parâmetros obrigatórios na URL, e :abc? indica que :abc é opcional e pode ter um valor padrão.

Show, já dentro do nosso componente, podemos recuperar esses valores da seguinte forma:

const { id, desc, abc = null } = useParams();

No caso do exemplos acima estamos definindo um valor de null de forma padrão para o abc, isto é, caso ele não tenha sido informado na URL.

Criando filtros nas rotas

Vão existir momentos em que você quer que o parâmetro :id seja exclusivamente um valor númerico, e não uma string ou qualquer outra coisa.

Sabendo disso, como podemos criar um filtro nas nossas rotas?

Você pode utilizar uma expressão regular (regex) na definição do caminho da rota. Isso garante que a rota só corresponda se o parâmetro :id for composto exclusivamente por dígitos numéricos. 

Por exemplo:

<Route path="/produto/:id(\d+)" element={<DetalhesDoProduto />} />

:id(\d+) especifica que o parâmetro :id deve ser composto por um ou mais dígitos numéricos (\d+).

A utilização de (\d+) dentro dos parênteses permite que o React Router DOM use essa expressão regular para validar o parâmetro :id.

Isso também significa que você pode utilizar expressões regulares dentro do atributo to do componente <Route />, incrível não?

Caso desejar, você pode fazer essa validação exclusivamente dentro do componente.

Para isso você deve definir o parâmetro :id normalmente:

<Route path="/produto/:id" element={<DetalhesDoProduto />} />

E dentro do seu componente (DetalhesDoProduto) realizar essa validação da seguinte forma:

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

function DetalhesDoProduto() {
 const history = useHistory();
 let { id } = useParams();

 // Função para validar se id é numérico
 const validarIdNumerico = (id) => {
 return /^\d+$/.test(id); // Expressão regular para verificar se é numérico
 };

 // Verificar se id é numérico
 if (!validarIdNumerico(id)) {
 // Redirecionar para página de erro ou para página inicial
 history.push('/not-found'); // Redireciona para a rota de not found
 // ou
 // history.push('/'); // Redireciona para a página inicial
 // Você pode ajustar conforme a estrutura de rotas da sua aplicação
 return null; // Retorna null para não renderizar o restante do componente
 }

 // Se id é válido, continua renderizando o componente
 return (
 <div>
 <h2>Detalhes do Produto</h2>
 <p>ID do Produto: {id}</p>
 {/* Renderização do restante do componente */}
 </div>
 );
}

export default DetalhesDoProduto;

Note que estamos usando o useHistory para redirecionar o usuário para a tela de erro, caso o :id fornecido não atenda as especificações.

E falando em useHistory, vamos aprender um pouco mais sobre ele 😉

Usando o useHistory

Antigamente, nas primeiras versões da biblioteca do React Router DOM nós tinhamos (e ainda temos) uma funcionalidade capaz de de permitir o acesso ao histórico de navegação do navegador, chamada de useHistory.

De modo a oferecer métodos para navegar para páginas anteriores, próximas, ou para qualquer outra rota configurada na sua aplicação.

Veja um exemplo simples da sua utilização:

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

function MeuComponente() {
 const history = useHistory();

 const handleClick = () => {
 // Navega para a rota '/outra-rota'
 history.push('/outra-rota');
 };

 return (
 <div>
 <h2>Meu Componente</h2>
 <button onClick={handleClick}>Ir para Outra Rota</button>
 </div>
 );
}

Ao clicar no botão, o usuário aciona a função handleClick que por sua vez chama o history.push que o redireciona para outra rota.

Veremos agora outros métodos que podemos usar no useHistory.

push(path, [state])

O método push adiciona uma nova entrada no histórico de navegação e navega para a rota especificada pelo path. O parâmetro opcional state permite passar dados de estado junto com a navegação.

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

function ExemploPush() {
 const history = useHistory();

 const handleClick = () => {
 // Navega para '/outra-rota' e passa um objeto de estado junto com a navegação
 history.push('/outra-rota', { id: 1, nome: 'Produto A' });
 };

 return (
 <div>
 <h2>Exemplo de push()</h2>
 <button onClick={handleClick}>Ir para Outra Rota com Estado</button>
 </div>
 );
}

replace(path, [state])

O método replace substitui a entrada atual no histórico de navegação pela nova rota especificada pelo path. Também aceita um parâmetro opcional state para passar dados de estado.

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

function ExemploReplace() {
 const history = useHistory();

 const handleClick = () => {
 // Substitui a entrada atual no histórico pela rota '/outra-rota' sem estado
 history.replace('/outra-rota');
 };

 return (
 <div>
 <h2>Exemplo de replace()</h2>
 <button onClick={handleClick}>Substituir para Outra Rota</button>
 </div>
 );
}

go(n), goBack() e goForward()

Esses métodos são utilizados para navegar entre as entradas no histórico de navegação.

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

function ExemploGo() {
 const history = useHistory();

 const handleGoBack = () => {
 // Navega para a página anterior no histórico
 history.goBack();
 };

 const handleGoForward = () => {
 // Navega para a próxima página no histórico
 history.goForward();
 };

 const handleGoCustom = () => {
 // Navega para uma entrada específica no histórico (neste caso, duas páginas para trás)
 history.go(-2);
 };

 return (
 <div>
 <h2>Exemplo de go(), goBack() e goForward()</h2>
 <button onClick={handleGoBack}>Voltar</button>
 <button onClick={handleGoForward}>Avançar</button>
 <button onClick={handleGoCustom}>Voltar 2 páginas</button>
 </div>
 );
}

history.goBack() navega para a página anterior no histórico de navegação.

history.goForward() navega para a próxima página no histórico de navegação.

history.go(-2) navega duas páginas para trás no histórico de navegação.

Usando o useNavigate

Apesar do useHistory ser muito legal, nas versões mais recentes do React Router DOM (versão 6 em diante), o useNavigate se tornou uma alternativa mais moderna e simplificada, visando melhorar a ergonomia e a facilidade de uso para navegação programática dentro dos componentes em ReactJS.

Enquanto o useHistory requer que você desestruture o objeto history para acessar os métodos de navegação (push, replace, goBack, etc.).

O useNavigate retorna diretamente uma função que você pode chamar para realizar a navegação entre rotas.

Seu funcionamento é bem simples:

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

function MeuComponente() {
 const navigate = useNavigate();

 const handleClick = () => {
 // Navega para a rota '/outra-rota'
 navigate('/outra-rota');
 };

 return (
 <div>
 <h2>Meu Componente com useNavigate</h2>
 <button onClick={handleClick}>Ir para Outra Rota</button>
 </div>
 );
}

Assim como o useHistory, o useNavigate possui alguns métodos:

navigate(to, options?): navega para a rota especificada pelo parâmetro to. Este método é usado para navegação principal entre rotas.

replace(to, options?): substitui a entrada atual no histórico de navegação pela rota especificada por to. Funciona da mesma forma que history.replace().

go(delta): navega para uma entrada no histórico de navegação relativa ao número delta, onde delta pode ser positivo para avançar ou negativo para retroceder.

goBack(): Equivalente a go(-1), navega para a página anterior no histórico de navegação.

goForward(): equivalente a go(1), navega para a próxima página no histórico de navegação.

veja um exemplo completo com todos os métodos mencionados acima:

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

function ExemploUseNavigate() {
 const navigate = useNavigate();

 const handleClick = () => {
 // Navega para a rota '/outra-rota' usando navigate()
 navigate('/outra-rota');
 };

 const handleReplace = () => {
 // Substitui a entrada atual no histórico pela rota '/outra-rota' usando replace()
 navigate('/outra-rota', { replace: true });
 };

 const handleGoBack = () => {
 // Navega para a página anterior usando goBack()
 navigate(-1);
 };

 const handleGoForward = () => {
 // Navega para a próxima página usando goForward()
 navigate(1);
 };

 return (
 <div>
 <h2>Exemplo de useNavigate</h2>
 <button onClick={handleClick}>Ir para Outra Rota</button>
 <button onClick={handleReplace}>Substituir para Outra Rota</button>
 <button onClick={handleGoBack}>Voltar</button>
 <button onClick={handleGoForward}>Avançar</button>
 </div>
 );
}

Incrível não acha? 😀

Otimizando a performance das nossas rotas com carregamento sob demanda

Uma das dúvidas mais pertinentes quando se trabalha com o React Router DOM é a seguinte:

"Será que a biblioteca carrega todas as páginas de uma só vez no navegador do usuário. Por exemplo: eu tenho 1001 páginas, então isso significa que todas as páginas são carregadas desde o início da aplicação?".

Apesar da pergunta acima estar totalmente equivocada, não, o React Router DOM não carregada todas de uma vez desde o início da aplicação, mesmo que haja muitas rotas configuradas.

Mas, você pode fazer o uso da estratégia de carregamento sob demanda em conjunto com o Lazy Loading.

Isso significa que cada rota pode ser configurada para carregar apenas quando necessário, reduzindo assim a carga inicial da aplicação. Isso é feito usando a função React.lazy() e <Suspense> (teremos uma lição futura para falar exclusivamente sobre esse componente) para componentes assíncronos.

Veja um exemplo disso tudo que falei acima sendo aplicado dentro do routes.js:

import { Routes, Route } from 'react-router-dom';
import { Suspense, lazy } from 'react';

const Home = lazy(() => import('../pages/Home'));
const Contato = lazy(() => import('../pages/Contato'));
const Servicos = lazy(() => import('../pages/Servicos'));
const Error404 = lazy(() => import('../pages/Error404'));
const DetalhesDoProduto = lazy(() => import('../pages/DetalhesDoProduto'));

function RouterApp() {
 return (
 <Routes>
 <Route path="/" element={
 <Suspense fallback={<div>Carregando...</div>}>
 <Home />
 </Suspense>
 } />
 <Route path="/servicos" element={
 <Suspense fallback={<div>Carregando...</div>}>
 <Servicos />
 </Suspense>
 } />
 <Route path="/contato" element={
 <Suspense fallback={<div>Carregando...</div>}>
 <Contato />
 </Suspense>
 } />
 <Route path="/produto/:id" element={
 <Suspense fallback={<div>Carregando...</div>}>
 <DetalhesDoProduto />
 </Suspense>
 } />
 <Route path="*" element={
 <Suspense fallback={<div>Carregando...</div>}>
 <Error404 />
 </Suspense>
 } />
 </Routes>
 );
}

export default RouterApp;

Este padrão de carregamento sob demanda ajuda a otimizar a performance da sua aplicação, garantindo que apenas os recursos necessários sejam carregados conforme os usuários navegam pelas diferentes rotas.

Sendo assim, certifique-se de que seu ambiente de desenvolvimento suporte o React.lazy() e <Suspense>, que são recursos do ReactJS para carregamento de código assíncrono.

Apesar disso, é importante ressaltar que o React Router DOM renderiza apenas o componente correspondente à rota atual.

Isso significa que mesmo que você tenha muitas rotas configuradas, apenas o componente da rota ativa será renderizado no momento.

Os outros componentes não são carregados ou renderizados até que a rota correspondente seja acessada.

Cuidados antes de se publicar uma aplicação que faz o uso do React Router DOM

Quando temos uma aplicação feita com React Router DOM, nós precisamos ter alguns cuidados a mais antes de fazer o deploy (npm run build) das nossas aplicações.

O primeiro cuidado que você deve ter, é sempre criar um arquivo chamado de _redirects dentro da pasta public do seu projeto em ReactJS:

Dentro desse arquivo, escreva o seguinte comando:

/* /index.html 200

E não se esqueça de gerar novamente o build da sua aplicação (npm run build) antes de mover os arquivos para o seu servidor.

A configuração /* /index.html 200 no React Router DOM, é um ajuste importante para garantir que o roteamento funcione corretamente em aplicações de página única (SPA).

Ela garante que todas as solicitações de URL sejam manipuladas pelo roteador do lado do cliente, permitindo uma navegação suave entre as diferentes "páginas" da aplicação, sem a necessidade de recarregar a página inteira a cada mudança de rota.

Além disso, nós desenvolvedores podemos fazer o uso de servidores com apache ou nginx para executar aplicações feitas com ReactJS.

Se a sua aplicação vai ser executada em baixo de um servidor apache, não se esqueça de configurar um arquivo .htaccess e colocá-lo dentro da pasta raiz da sua aplicação. Segue as configurações do arquivo:

RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]

Não se esqueça também de habilitar o .mod_rewrite no servidor para que o seu arquivo .htaccess seja reconhecido.

Agora, se você faz o uso de um servidor nginx, você vai precisar fazer algumas modificações no arquivo .conf do seu virtual host, por exemplo:

server {
 listen 80;
 server_name seu-domínio.com; # Substitua pelo seu domínio

 root /caminho/para/o/diretório/do/seu/aplicativo; # Substitua pelo caminho correto

 index index.html;

 location / {
 try_files $uri $uri/ /index.html;
 }
}

Feito isso, você garante que a biblioteca reconheça suas rotas normalmente 😌

Usando contextos em conjunto com rotas

Em lições passadas, nós aprendemos a criar contextos em nossas aplicações em ReactJS.

Com isso, talvez você esteja se perguntando: "Como eu insiro um contexto dentro do React Router DOM? Eu faço isso antes do <BrowserRouter> ou depois dele?".

A resposta para essa pergunta é... DEPENDE!

Contexto antes do <BrowserRouter>

Se o contexto que você deseja fornecer deve afetar todos os componentes renderizados dentro do <BrowserRouter>, então você deve envolver ambos com o contexto dentro do App.js, Por exemplo:

export default function App() {
 return (
 <MeuContextoProvider>
 <BrowserRouter>
 <RouterApp /> 
 </BrowserRouter>
 </MeuContextoProvider>
 );
}

Dessa forma, todos as rotas teriam acesso ao seu contexto.

Contexto depois do <BrowserRouter>

Se o contexto precisa ser aplicado apenas a um subconjunto específico de componentes ou rotas dentro do <BrowserRouter>, então você deve envolver somente esses componentes com o contexto dentro do routes.js.

return (
 <Router>
 <Routes>
 <MeuContextoProvider>
 <Route path="/rota-a" element={<ComponenteA />} />
 <Route path="/rota-b" element={<ComponenteB />} />
 </MeuContextoProvider>
 {/* Outras rotas aqui que não devem ser administradas pelo contexto */}
 </Routes>
 </Router>
);

Dessa forma, somente algumas rotas teriam acesso ao seu contexto (ou outros contextos diferentes).

Usando o TanStackQuery em conjunto com rotas

Em lições passadas, nós aprendemos a utilizar a biblioteca TanStackQuery (antiga React Query) para gerenciar todas as nossas requisições HTTP.

E se você se lembra bem... essa biblioteca nada mais é do que um CONTEXTO.

Sendo assim, basta aplicar a mesma lógica que vimos no tópico anterior.

1) Antes do BrowserRoute:

<QueryClientProvider client={queryClient}>
 <BrowserRouter>
 <RouterApp /> 
 </BrowserRouter>
</QueryClientProvider>

Para que toda a sua aplicação consiga ter acesso a biblioteca.

2) Depois do BrowserRoute:

 <Router>
 <Routes>
 <QueryClientProvider client={queryClient}>
 <Route path="/rota-a" element={<ComponenteA />} />
 <Route path="/rota-b" element={<ComponenteB />} />
 </QueryClientProvider>
 {/* Outras rotas aqui que não devem ser administradas pelo contexto */}
 </Routes>
</Router>

Para que apenas uma parte da sua aplicação consiga ter acesso a biblioteca.

Viu como é simples? 🤩

Arquivos da lição

Os arquivos dessa lição podem ser encontrados neste repositório do GitHub.

Conclusão

Creio que depois dessa aula você já será capaz de criar uma aplicação com multiplas páginas 🤩

E parabéns por isso, você acaba de dar um passo super importante na sua jornada de desenvolvedor ReactJS 🙌

Na próxima lição, vamos aprender a criar rotas privadas, ou seja, rotas que só podem ser acessados quando uma condição for verdade.

Spoiler: Vamos aprender a base de um sistema de login 🥳

Te aguardo na próxima lição!