Explore padrões de navegação essenciais com o React Router v6. Aprenda sobre roteamento declarativo, rotas dinâmicas, navegação programática, rotas aninhadas e estratégias de carregamento de dados para construir aplicações web robustas e amigáveis.
React Router v6: Dominando Padrões de Navegação para Aplicações Web Modernas
O React Router v6 é uma biblioteca de roteamento poderosa e flexível para aplicações React. Ele permite que você crie aplicações de página única (SPAs) com uma experiência de usuário fluida, gerenciando a navegação sem recargas completas da página. Este post de blog irá aprofundar-se nos padrões de navegação essenciais usando o React Router v6, fornecendo o conhecimento e os exemplos para construir aplicações web robustas e amigáveis.
Entendendo os Conceitos Fundamentais do React Router v6
Antes de mergulhar em padrões específicos, vamos rever alguns conceitos fundamentais:
- Roteamento Declarativo: O React Router utiliza uma abordagem declarativa, onde você define suas rotas como componentes React. Isso torna sua lógica de roteamento clara e de fácil manutenção.
- Componentes: Os componentes principais incluem
BrowserRouter
,HashRouter
,MemoryRouter
,Routes
eRoute
. - Hooks: O React Router fornece hooks como
useNavigate
,useLocation
,useParams
euseRoutes
para acessar informações de roteamento e manipular a navegação.
1. Roteamento Declarativo com <Routes>
e <Route>
A base do React Router v6 está no roteamento declarativo. Você define suas rotas usando os componentes <Routes>
e <Route>
. O componente <Routes>
atua como um contêiner para suas rotas, e o componente <Route>
define uma rota específica e o componente a ser renderizado quando essa rota corresponde à URL atual.
Exemplo: Configuração Básica de Rotas
Aqui está um exemplo básico de configuração de rotas para uma aplicação simples:
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Contact from "./pages/Contact";
function App() {
return (
} />
} />
} />
);
}
export default App;
Neste exemplo, definimos três rotas:
/
: Renderiza o componenteHome
./about
: Renderiza o componenteAbout
./contact
: Renderiza o componenteContact
.
O componente BrowserRouter
habilita o roteamento baseado no histórico do navegador. O React Router compara a URL atual com as rotas definidas e renderiza o componente correspondente.
2. Rotas Dinâmicas com Parâmetros de URL
Rotas dinâmicas permitem criar rotas que podem lidar com diferentes valores na URL. Isso é útil para exibir conteúdo com base em um identificador único, como o ID de um produto ou de um usuário. O React Router v6 usa o símbolo :
para definir parâmetros de URL.
Exemplo: Exibindo Detalhes do Produto
Digamos que você tenha uma aplicação de e-commerce e queira exibir os detalhes de cada produto com base em seu ID. Você pode definir uma rota dinâmica como esta:
import { BrowserRouter, Routes, Route, useParams } from "react-router-dom";
function ProductDetails() {
const { productId } = useParams();
// Fetch product details based on productId
// ...
return (
Product Details
Product ID: {productId}
{/* Display product details here */}
);
}
function App() {
return (
} />
);
}
export default App;
Neste exemplo:
/products/:productId
define uma rota dinâmica onde:productId
é um parâmetro de URL.- O hook
useParams
é usado para acessar o valor do parâmetroproductId
dentro do componenteProductDetails
. - Você pode então usar o
productId
para buscar os detalhes do produto correspondente na sua fonte de dados.
Exemplo de Internacionalização: Lidando com Códigos de Idioma
Para um site multilíngue, você pode usar uma rota dinâmica para lidar com códigos de idioma:
} />
Esta rota corresponderia a URLs como /en/about
, /fr/about
e /es/about
. O parâmetro lang
pode então ser usado para carregar os recursos de idioma apropriados.
3. Navegação Programática com useNavigate
Embora o roteamento declarativo seja ótimo para links estáticos, muitas vezes você precisa navegar programaticamente com base em ações do usuário ou na lógica da aplicação. O React Router v6 fornece o hook useNavigate
para esse propósito. O useNavigate
retorna uma função que permite navegar para diferentes rotas.
Exemplo: Redirecionando Após o Envio de um Formulário
Digamos que você tenha um envio de formulário e queira redirecionar o usuário para uma página de sucesso após o envio bem-sucedido:
import { useNavigate } from "react-router-dom";
function MyForm() {
const navigate = useNavigate();
const handleSubmit = async (event) => {
event.preventDefault();
// Submit the form data
// ...
// Redirect to the success page after successful submission
navigate("/success");
};
return (
);
}
export default MyForm;
Neste exemplo:
- Usamos o hook
useNavigate
para obter a funçãonavigate
. - Após o envio bem-sucedido do formulário, chamamos
navigate("/success")
para redirecionar o usuário para a rota/success
.
Passando Estado Durante a Navegação
Você também pode passar estado junto com a navegação usando o segundo argumento de navigate
:
navigate("/confirmation", { state: { orderId: "12345" } });
Isso permite que você passe dados para o componente de destino, que podem ser acessados usando o hook useLocation
.
4. Rotas Aninhadas e Layouts
Rotas aninhadas permitem criar estruturas de roteamento hierárquicas, onde uma rota está aninhada dentro de outra. Isso é útil para organizar aplicações complexas com múltiplos níveis de navegação. Isso ajuda na criação de layouts onde certos elementos de UI estão consistentemente presentes em uma seção da aplicação.
Exemplo: Seção de Perfil de Usuário
Digamos que você tenha uma seção de perfil de usuário com rotas aninhadas para exibir as informações do perfil do usuário, configurações e pedidos:
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
function Profile() {
return (
Perfil de Usuário
-
Informações do Perfil
-
Configurações
-
Pedidos
} />
} />
} />
);
}
function ProfileInformation() {
return Componente de Informações do Perfil
;
}
function Settings() {
return Componente de Configurações
;
}
function Orders() {
return Componente de Pedidos
;
}
function App() {
return (
} />
);
}
export default App;
Neste exemplo:
- A rota
/profile/*
corresponde a qualquer URL que comece com/profile
. - O componente
Profile
renderiza um menu de navegação e um componente<Routes>
para lidar com as rotas aninhadas. - As rotas aninhadas definem os componentes a serem renderizados para
/profile/info
,/profile/settings
e/profile/orders
.
O *
na rota pai é crucial; ele significa que a rota pai deve corresponder a qualquer sub-caminho, permitindo que as rotas aninhadas sejam correspondidas corretamente dentro do componente Profile
.
5. Lidando com Erros "Não Encontrado" (404)
É essencial lidar com casos em que o usuário navega para uma rota que não existe. O React Router v6 facilita isso com uma rota "catch-all" (pega-tudo).
Exemplo: Implementando uma Página 404
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
function NotFound() {
return (
404 - Não Encontrado
A página que você está procurando não existe.
Voltar para a página inicial
);
}
function App() {
return (
} />
} />
} />
);
}
Neste exemplo:
- A rota
<Route path="*" element={<NotFound />} />
é uma rota "catch-all" que corresponde a qualquer URL que não corresponda a nenhuma das outras rotas definidas. - É importante colocar esta rota no final do componente
<Routes>
para que ela só corresponda se nenhuma outra rota for correspondida.
6. Estratégias de Carregamento de Dados com o React Router v6
O React Router v6 não inclui mecanismos de carregamento de dados integrados como seu antecessor (React Router v5 com `useRouteMatch`). No entanto, ele fornece as ferramentas para implementar várias estratégias de carregamento de dados de forma eficaz.
Opção 1: Buscando Dados nos Componentes
A abordagem mais simples é buscar dados diretamente no componente que renderiza a rota. Você pode usar o hook useEffect
para buscar dados quando o componente é montado ou quando os parâmetros da URL mudam.
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
function ProductDetails() {
const { productId } = useParams();
const [product, setProduct] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchProduct() {
try {
const response = await fetch(`/api/products/${productId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setProduct(data);
setLoading(false);
} catch (e) {
setError(e);
setLoading(false);
}
}
fetchProduct();
}, [productId]);
if (loading) return Carregando...
;
if (error) return Erro: {error.message}
;
if (!product) return Produto não encontrado
;
return (
{product.name}
{product.description}
);
}
export default ProductDetails;
Esta abordagem é direta, mas pode levar à duplicação de código se você precisar buscar dados em vários componentes. Também é menos eficiente porque a busca de dados começa apenas depois que o componente é montado.
Opção 2: Usando um Hook Personalizado para Busca de Dados
Para reduzir a duplicação de código, você pode criar um hook personalizado que encapsula a lógica de busca de dados. Este hook pode então ser reutilizado em múltiplos componentes.
import { useState, useEffect } from "react";
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const json = await response.json();
setData(json);
setLoading(false);
} catch (e) {
setError(e);
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
Então, você pode usar este hook em seus componentes:
import { useParams } from "react-router-dom";
import useFetch from "./useFetch";
function ProductDetails() {
const { productId } = useParams();
const { data: product, loading, error } = useFetch(`/api/products/${productId}`);
if (loading) return Carregando...
;
if (error) return Erro: {error.message}
;
if (!product) return Produto não encontrado
;
return (
{product.name}
{product.description}
);
}
export default ProductDetails;
Opção 3: Usando uma Biblioteca de Roteamento com Capacidades de Busca de Dados (TanStack Router, Remix)
Bibliotecas como TanStack Router e Remix oferecem mecanismos de busca de dados integrados que se integram perfeitamente com o roteamento. Essas bibliotecas geralmente fornecem recursos como:
- Loaders (Carregadores): Funções que são executadas *antes* de uma rota ser renderizada, permitindo que você busque dados e os passe para o componente.
- Actions (Ações): Funções que lidam com envios de formulários e mutações de dados.
Usar uma biblioteca como essa pode simplificar drasticamente o carregamento de dados e melhorar o desempenho, especialmente para aplicações complexas.
Renderização no Lado do Servidor (SSR) e Geração de Site Estático (SSG)
Para melhorar o SEO e o desempenho do carregamento inicial, considere o uso de SSR ou SSG com frameworks como Next.js ou Gatsby. Esses frameworks permitem que você busque dados no servidor ou durante o tempo de construção e sirva HTML pré-renderizado para o cliente. Isso elimina a necessidade de o cliente buscar dados no carregamento inicial, resultando em uma experiência mais rápida e amigável para SEO.
7. Trabalhando com Diferentes Tipos de Router
O React Router v6 fornece diferentes implementações de router para se adequar a vários ambientes e casos de uso:
- BrowserRouter: Usa a API de histórico do HTML5 (
pushState
,replaceState
) para navegação. É a escolha mais comum para aplicações web executadas em um ambiente de navegador. - HashRouter: Usa a parte hash da URL (
#
) para navegação. Isso é útil para aplicações que precisam suportar navegadores mais antigos ou que são implantadas em servidores que não suportam a API de histórico do HTML5. - MemoryRouter: Mantém o histórico da sua "URL" на memória (um array de URLs). Útil em ambientes como React Native e para testes.
Escolha o tipo de router que melhor se adapta aos requisitos e ao ambiente da sua aplicação.
Conclusão
O React Router v6 fornece uma solução de roteamento abrangente e flexível para aplicações React. Ao entender e aplicar os padrões de navegação discutidos neste post de blog, você pode construir aplicações web robustas, amigáveis e de fácil manutenção. Desde o roteamento declarativo com <Routes>
e <Route>
até rotas dinâmicas com parâmetros de URL, navegação programática com useNavigate
e estratégias eficazes de carregamento de dados, o React Router v6 capacita você a criar experiências de navegação fluidas para seus usuários. Considere explorar bibliotecas de roteamento avançadas e frameworks SSR/SSG para um controle e otimização de desempenho ainda maiores. Lembre-se de adaptar esses padrões aos requisitos específicos da sua aplicação e sempre priorizar uma experiência de usuário clara e intuitiva.