React: React Router: Tópicos avançados
Última atualização: 2026-08-26
Tom implementou com sucesso o roteamento básico, mas novos requisitos continuam surgindo: os usuários precisam ser redirecionados automaticamente para o painel após o login; certas páginas exigem login para acesso; à medida que o aplicativo cresce, a página inicial demora mais para carregar; e a página da lista de produtos precisa suportar parâmetros de filtro na URL... Ele precisa aprender os recursos avançados do React Router para lidar com esses cenários do mundo real.
1. O que você vai aprender
- useNavigate: Navegação programática (redirecionamento após login, botões “Voltar” e “Avançar”)
- O componente Route Guard implementa a autenticação e a autorização
- React.lazy + Suspense para carregamento sob demanda
- useSearchParams: Gerenciar parâmetros de consulta da URL
- Estratégias para organizar e extrair configurações de roteamento
2. Diagramas conceituais
flowchart TD
U[User Actions] --> A{Are you logged in??}
A -->|Not logged in| B[ProtectedRoute<br/>→ Redirect to /login]
A -->|Logged in| C{Permissions?}
C -->|No permission| D[→ 403 Page]
C -->|Has permission| E[Load the target page]
E --> F{Is the component lazily loaded??}
F -->|is | G[Suspense<br/>Show loading]
G --> H[Rendering Page]
F -->|No| H
style A fill:#fff3e0,stroke:#f57c00
style B fill:#ffcdd2,stroke:#d32f2f
style G fill:#e1f5fe,stroke:#0288d1
style H fill:#e8f5e9,stroke:#388e3c
Solicitação do usuário → O Route Guard verifica o login e as permissões → A página é carregada após a aprovação (exibe “carregando” durante o carregamento diferido) → Renderização final.
3. Um cenário da vida real
O aplicativo do Tom exige o seguinte: após um login bem-sucedido, o usuário deve ser redirecionado automaticamente para o painel de controle; a página do painel de controle deve ser protegida por login; somente usuários com a função “admin” devem ter acesso ao painel de administração; a página inicial deve carregar rapidamente na primeira tela (com páginas maiores carregadas sob demanda); e as URLs das páginas de lista de produtos devem incluir parâmetros de filtragem e paginação para facilitar o compartilhamento.
(1) Navegação programática: useNavigate
Tom precisa processar os envios de formulários na página de login e, caso o envio seja bem-sucedido, redirecionar os usuários para páginas diferentes, de acordo com suas funções. Esse tipo de navegação, acionada pela lógica do código, não pode usar <Link> (já que os links só podem ser acionados por cliques dos usuários); em vez disso, deve-se usar useNavigate.
useNavigate Retorna uma função navigate que suporta três métodos de chamada:
| Uso | Efeito | Exemplo |
|---|---|---|
navigate('/path') |
Ir para um caminho especificado (adicionar uma nova entrada à pilha do histórico) | navigate('/dashboard') |
navigate('/path', { replace: true }) |
Substituir o histórico atual (não é possível voltar a este ponto) | Redirecionar após o login |
navigate(-1) |
Voltar | Voltar à página anterior |
▶ Exemplo 1: Navegação programática após o login
import { useNavigate, useLocation } from 'react-router-dom'
function LoginPage() {
const navigate = useNavigate()
const location = useLocation()
// from URL Retrieve the URL to redirect to after login from the query parameters
const from = location.state?.from?.pathname || '/dashboard'
function handleLogin(event) {
event.preventDefault()
const formData = new FormData(event.target)
const username = formData.get('username')
// Simulate a login request
fakeLogin(username).then(user => {
if (user.role === 'admin') {
navigate('/admin', { replace: true }) // Replace Record,Cannot go back to the login page
} else {
navigate(from, { replace: true }) // Redirect to the page you were trying to access before logging in
}
})
}
// Login Form
return (
<form onSubmit={handleLogin}>
<input name="username" placeholder="Username" required />
<button type="submit">Log In</button>
<button type="button" onClick={() => navigate(-1)}>Back</button>
</form>
)
}
// Simulated Login API
async function fakeLogin(username) {
await new Promise(r => setTimeout(r, 500))
return { name: username, role: username === 'admin' ? 'admin' : 'user' }
}
A função do useLocation: location.state Ele pode receber dados de estado passados pelo Link ou pelo navigate. Por exemplo, ao redirecionar para uma página de login dentro de um ProtectedRoute, é possível armazenar o caminho da página que o usuário estava tentando acessar originalmente em state, de modo que, após o login, o usuário seja redirecionado automaticamente de volta para essa página.
(2) Proteção de rota: ProtectedRoute
| Tipo de proteção | Lógica de verificação | Comportamento em caso de falha | Cenários típicos |
|---|---|---|---|
| Login Guard | isAuthenticated |
Redirecionar para /login |
Painel, Minha conta |
| Permissions Guard | user.role === 'admin' |
Redirecionar para /403 |
Painel de administração |
| Proteção de botões | featureFlags.xEnabled |
Redirecionar para /upgrade |
Recursos pagos |
| Verificação condicional | profileComplete |
Redirecionar para /onboarding |
Guia para o primeiro login |
A página do painel do Tom exige que o usuário faça login para acessá-la, e o painel de administração requer a função “admin”. Ele precisa de um mecanismo de “proteção” — que verifique o status do usuário antes de exibir a página e o redirecione caso as condições não sejam atendidas.
Em essência, o RouteGuard é um componente wrapper: ele aceita componentes filhos e executa a lógica de validação antes da renderização. Se a validação for bem-sucedida, ele renderiza os componentes filhos; caso contrário, ele redireciona para outra página usando <Navigate>.
▶ Exemplo 2: Proteção de roteamento em várias camadas
import { Navigate, useLocation } from 'react-router-dom'
// Simulated Certification Hook
function useAuth() {
return {
user: { name: 'Tom', role: 'admin' }, // In actual projects, starting from Context or store Get
isAuthenticated: true
}
}
// First Floor:Login Guard
function ProtectedRoute({ children }) {
const { isAuthenticated } = useAuth()
const location = useLocation()
if (!isAuthenticated) {
// Save the user's destination to state in ,Redirect back after logging in
return <Navigate to="/login" state={{ from: location }} replace />
}
return children
}
// Second Floor:Character Guard
function AdminRoute({ children }) {
const { user } = useAuth()
if (user.role !== 'admin') {
return <Navigate to="/403" replace />
}
return children
}
// Used in router configuration
function AppRoutes() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/403" element={<AccessDenied />} />
{/* You must log in */}
<Route path="/dashboard" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
{/* You must log in + admin Character */}
<Route path="/admin" element={
<ProtectedRoute>
<AdminRoute>
<AdminPanel />
</AdminRoute>
</ProtectedRoute>
} />
</Routes>
)
}
Padrão de Proteção Aninhada: O ProtectedRoute externo verifica “se o usuário está conectado”, enquanto o AdminRoute interno verifica “se o usuário possui permissões de administrador”. Isso separa as responsabilidades e permite a reutilização. Se a função “Editar” for necessária no futuro, basta adicionar um EditorRoute.
(3) Carregamento diferido: React.lazy + Suspense
À medida que o aplicativo crescia, Tom percebeu que a página inicial estava demorando cada vez mais para carregar — como todo o código de todas as páginas estava agrupado em um único pacote, os usuários precisavam baixar o código do painel de administração sempre que acessavam a página inicial. O React.lazy permite que o código dos componentes seja dividido em partes separadas, que são carregadas somente quando necessário.
import { lazy, Suspense } from 'react'
// These components will not be bundled into the main bundle in
const Dashboard = lazy(() => import('./pages/Dashboard'))
const Settings = lazy(() => import('./pages/Settings'))
const AdminPanel = lazy(() => import('./pages/AdminPanel'))
const UserList = lazy(() => import('./pages/UserList'))
▶ Exemplo 3: Carregamento diferido no nível da rota
import { lazy, Suspense } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
// Lazy-load all page components
const Home = lazy(() => import('./pages/Home'))
const Dashboard = lazy(() => import('./pages/Dashboard'))
const ProductList = lazy(() => import('./pages/ProductList'))
const ProductDetail = lazy(() => import('./pages/ProductDetail'))
const Settings = lazy(() => import('./pages/Settings'))
const NotFound = lazy(() => import('./pages/NotFound'))
// Loading component
function PageLoader() {
return (
<div style={{
display: 'flex', justifyContent: 'center', alignItems: 'center',
height: '100vh', fontSize: '1.2rem', color: '#666'
}}>
<div className="spinner" />
<span style={{ marginLeft: '12px' }}>Page is loading...</span>
</div>
)
}
function App() {
return (
<BrowserRouter>
{/* Suspense Wrap all lazy-loading routes */}
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/products" element={<ProductList />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/settings" element={<Settings />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Suspense>
</BrowserRouter>
)
}
export default App
Como funciona o carregamento diferido: O React carrega dinamicamente o bloco /dashboard apenas quando um usuário acessa /dashboard pela primeira vez. Durante o carregamento, é exibido o componente fallback do Suspense. Assim que o carregamento for concluído, ele é substituído pelo componente Dashboard propriamente dito.
Benefícios de desempenho: Supondo que o pacote original tenha 500 KB, o carregamento diferido reduz o pacote principal para 100 KB, com cada página pesando cerca de 80 KB. Quando os usuários acessam a página inicial, precisam baixar apenas 100 KB em vez de 500 KB — o que resulta em uma melhoria de aproximadamente 5 vezes na velocidade de carregamento da parte visível da página.
4. useSearchParams: Gerenciamento de parâmetros de consulta de URL
| Método de parâmetro de URL | API | Exemplo de sintaxe | Dados aplicáveis |
|---|---|---|---|
| Parâmetro de caminho | useParams() |
/products/:id → { id: '42' } |
Identificador obrigatório (ID do recurso) |
| Parâmetros de consulta | useSearchParams() |
?sort=price&page=2 |
Filtragem/classificação/paginação opcionais |
| Hash | useLocation().hash |
#section-3 |
Links de âncora da página |
| Estado | useLocation().state |
navigate('/path', { state }) |
Transmissão de dados ocultos entre páginas |
A página da lista de produtos do Tom precisa oferecer recursos como filtragem por categoria, ordenação por preço e paginação, e esses critérios de filtragem devem constar na URL — para que os usuários possam compartilhar os links filtrados com outras pessoas.
(1) Leitura e gravação de parâmetros de consulta
useSearchParams Retorna um objeto semelhante a um Map que permite ler e gravar parâmetros de consulta de URL da mesma forma que Map. Assim como useState, ele retorna um valor e um setter.
import { useSearchParams } from 'react-router-dom'
function ProductList() {
const [searchParams, setSearchParams] = useSearchParams()
const category = searchParams.get('category') || 'All'
const sort = searchParams.get('sort') || 'default'
const page = Number(searchParams.get('page')) || 1
function updateFilter(key, value) {
setSearchParams(prev => {
if (value) prev.set(key, value)
else prev.delete(key)
return prev
})
}
return (
<div>
<p>Current Category:{category} | Sort:{sort} | Page: {page}</p>
<button onClick={() => updateFilter('category', 'Electronics')}>Electronics Categories</button>
<button onClick={() => updateFilter('sort', 'price')}>Sort by Price</button>
<button onClick={() => updateFilter('page', String(page + 1))}>Next Page</button>
<button onClick={() => setSearchParams({})}>Clear Filters</button>
</div>
)
}
// URL It will be updated in real time:/products?category=Electronics&sort=price&page=2
▶ Exemplo 4: Funcionalidade completa de filtragem de produtos
import { useSearchParams } from 'react-router-dom'
// Simulated Product Data
const allProducts = [
{ id: 1, name: 'iPhone 16', category: 'Electronics', price: 6999 },
{ id: 2, name: 'React Programming Books', category: 'Books', price: 79 },
{ id: 3, name: 'Mechanical Keyboard', category: 'Electronics', price: 399 },
{ id: 4, name: 'Introduction to Design Patterns', category: 'Books', price: 59 },
{ id: 5, name: 'Bluetooth Headphones', category: 'Electronics', price: 899 },
]
function ProductListPage() {
const [searchParams, setSearchParams] = useSearchParams()
// from URL Read Filter Criteria
const category = searchParams.get('category') || ''
const sortBy = searchParams.get('sort') || 'name'
const page = parseInt(searchParams.get('page') || '1', 10)
const pageSize = 3
// Filtering
let filtered = category
? allProducts.filter(p => p.category === category)
: allProducts
// Sort
if (sortBy === 'price') {
filtered = [...filtered].sort((a, b) => a.price - b.price)
} else {
filtered = [...filtered].sort((a, b) => a.name.localeCompare(b.name))
}
// Pagination
const totalPages = Math.ceil(filtered.length / pageSize)
const paged = filtered.slice((page - 1) * pageSize, page * pageSize)
// Update Filter Criteria
function setFilter(key, value) {
setSearchParams(prev => {
const next = new URLSearchParams(prev)
if (value) {
next.set(key, value)
} else {
next.delete(key)
}
next.set('page', '1') // Return to the first page when switching filters
return next
})
}
return (
<div>
<div style={{ marginBottom: '16px' }}>
<label>Categories:
<select value={category} onChange={e => setFilter('category', e.target.value)}>
<option value="">All</option>
<option value="Electronics">Electronics</option>
<option value="Books">Books</option>
</select>
</label>
<label style={{ marginLeft: '16px' }}>Sort:
<select value={sortBy} onChange={e => setFilter('sort', e.target.value)}>
<option value="name">Name</option>
<option value="price">Price</option>
</select>
</label>
</div>
<ul>
{paged.map(p => (
<li key={p.id}>{p.name} — ${p.price}({p.category})</li>
))}
</ul>
<div>
{Array.from({ length: totalPages }, (_, i) => (
<button
key={i}
onClick={() => setSearchParams(prev => {
const next = new URLSearchParams(prev)
next.set('page', String(i + 1))
return next
})}
style={{ fontWeight: page === i + 1 ? 'bold' : 'normal' }}
>
{i + 1}
</button>
))}
</div>
<p>Currently URL:/products?category={category}&sort={sortBy}&page={page}</p>
</div>
)
}
Palavra-chave: Os parâmetros de consulta da URL representam um “estado compartilhável”. Depois que um usuário aplica filtros e copia a URL para enviar a um colega, esse colega verá exatamente os mesmos resultados filtrados ao abrir o link. Isso não é possível ao usar useState para gerenciar condições de filtro.
5. Estratégias organizacionais para a configuração de roteamento
Quando o número de rotas do projeto chega a várias dezenas, ter todas as rotas escritas em um único componente torna o código difícil de manter. Tom precisa extrair a configuração das rotas para um módulo separado.
(1) Arquivo de configuração de roteamento
// src/routes/index.js
import { lazy } from 'react'
// Centrally manage all routing definitions
const routes = [
{
path: '/',
component: lazy(() => import('../pages/Home')),
exact: true
},
{
path: '/login',
component: lazy(() => import('../pages/Login')),
},
{
path: '/dashboard',
component: lazy(() => import('../pages/Dashboard')),
protected: true // You must log in
},
{
path: '/admin',
component: lazy(() => import('../pages/AdminPanel')),
protected: true,
adminOnly: true // Required admin Permissions
},
{
path: '/products',
component: lazy(() => import('../pages/ProductList')),
},
{
path: '/products/:id',
component: lazy(() => import('../pages/ProductDetail')),
},
{
path: '*',
component: lazy(() => import('../pages/NotFound')),
}
]
export default routes
▶ Exemplo 5: Renderizador de rota
// src/routes/AppRouter.jsx
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { Suspense } from 'react'
import routes from './index'
import ProtectedRoute from '../components/ProtectedRoute'
import AdminRoute from '../components/AdminRoute'
function renderRoutes(routeList) {
return routeList.map(route => {
const Component = route.component
let element = <Component />
// On-Demand Package Guard
if (route.protected) {
element = <ProtectedRoute>{element}</ProtectedRoute>
}
if (route.adminOnly) {
element = <AdminRoute>{element}</AdminRoute>
}
return (
<Route key={route.path} path={route.path} element={element} />
)
})
}
function AppRouter() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading......</div>}>
<Routes>
{renderRoutes(routes)}
</Routes>
</Suspense>
</BrowserRouter>
)
}
export default AppRouter
A vantagem dessa abordagem é que as configurações de roteamento ficam centralizadas em um único local, a lógica de proteção é encapsulada automaticamente e, para adicionar uma nova página, basta adicionar um objeto ao routes/index.js.
(2) useLocation: monitorar alterações na rota
Além da navegação, o Tom também precisa monitorar mudanças na rota em determinados cenários — por exemplo, para enviar dados de rastreamento quando o caminho da página muda, fechar janelas pop-up ou redefinir o estado dos formulários. O useLocation pode recuperar as informações da URL atual e, quando usado em conjunto com o useEffect, monitorar mudanças no caminho.
import { useLocation } from 'react-router-dom'
import { useEffect } from 'react'
function PageTracker() {
const location = useLocation()
useEffect(() => {
// Triggered whenever the path changes
console.log('Page Views:', location.pathname + location.search)
// Tracking Event Reporting
analytics.pageView({
path: location.pathname,
search: location.search,
timestamp: Date.now()
})
}, [location]) // Dependency location Object
// Note:location.pathname or location.search Any change will trigger
return null // This component does not render anything UI
}
// in App Used in
function App() {
return (
<BrowserRouter>
<PageTracker /> {/* Place Routes External,Always Listen */}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</BrowserRouter>
)
}
Principais propriedades retornadas por useLocation: pathname (caminho, como /products/42), search (string de consulta, como ?category=electronics), hash (hash da URL, como #section-2), state (dados de estado passados por meio de Link ou navigate).
❓ Perguntas Frequentes
P: Como faço para escolher entre
useNavigateeLink? R:Linké usado para navegação acionada por cliques do usuário (como links na barra de navegação, trilha de navegação ou listas de artigos).useNavigateé usado para navegação acionada por lógica de código (como redirecionamentos após o login, após o envio de formulários, redirecionamentos programados ou navegação para trás/para frente). Regra básica: interação do usuário →Link; lógica de código →useNavigate.
P: Os guardas de rota podem ser aninhados em vários níveis? Há alguma precaução que deva ser levada em conta? R: Sim. Um padrão comum é que o guarda externo
ProtectedRouteverifique “se o usuário está conectado”, enquanto o guarda internoAdminRouteverifica “se o usuário possui permissões de administrador”. Ao aninhar guardas, certifique-se de que cada guarda tenha uma única responsabilidade; não verifique tanto o status de login quanto as permissões de função dentro de um único guarda. Se houver guardas demais, considere usar um array de configuração combinado com um loop para envolver automaticamente várias camadas de guardas.
P: Quais são as limitações do carregamento diferido do React.lazy? R: Existem três limitações: (1) Ele só pode ser usado com componentes exportados por meio da exportação padrão (
export default); (2) Deve ser usado dentro de um componente Suspense; (3) Não oferece suporte à renderização no lado do servidor (para SSR, use@loadable/componentem vez disso). Além disso, se a conexão de rede estiver lenta enquanto um componente carregado de forma preguiçosa estiver sendo carregado, os usuários verão o conteúdo de fallback; recomenda-se que o fallback seja projetado para ser pequeno e rápido.
P: Qual é a diferença entre usar
useSearchParamseuseStatepara gerenciar condições de filtro? R:useSearchParamssincroniza o estado com a URL — os usuários podem compartilhar links, adicionar páginas aos favoritos e navegar para frente e para trás. O estado gerenciado poruseStateexiste apenas na memória e é perdido quando a página é atualizada. No entanto,useSearchParamsacarreta uma sobrecarga de desempenho (mudanças na URL acionam a re-renderização do componente). Para cenários com atualizações frequentes (como arrastar um controle deslizante), recomenda-se usaruseStateprimeiro e, em seguida, sincronizar o estado com a URL assim que ele for confirmado.
P: Qual é a diferença entre route guards e middleware? R: Route guards são interceptadores no nível do componente React — eles envolvem o componente alvo e, após verificarem as condições, decidem se devem renderizar o componente ou redirecionar. Middleware são interceptadores no nível do servidor do Next.js — eles executam lógica (como autenticação, redirecionamento e testes A/B) depois que uma solicitação chega ao servidor, mas antes que a página seja renderizada. Os guardas do React Router são executados no lado do cliente, enquanto o middleware do Next.js é executado no lado do servidor, no Edge Runtime.
📖 Resumo
- O
useNavigatelida com a navegação acionada por código (redirecionamento após login, navegação para trás e para frente),replace: truepara evitar que a pilha do histórico seja corrompida - A proteção do roteador é implementada por meio de um componente wrapper; a camada externa verifica a autenticação, enquanto a camada interna verifica as permissões, garantindo o princípio da responsabilidade única e a reutilização.
- O React.lazy + Suspense permite o agrupamento de código por rota, melhorando significativamente a velocidade de carregamento da primeira tela
- O
useSearchParamssincroniza as configurações de filtro e paginação com a URL, permitindo o compartilhamento de links e a navegação para frente e para trás no navegador - Gerenciamento centralizado das configurações de rotas + proteções automáticas de renderização, ideal para a manutenção de projetos de grande porte
📝 Exercícios
- Implementar uma página de login: Depois que o usuário digitar seu nome de usuário, use
useNavigatepara navegar até o painel de controle e usereplace: truedurante a navegação para impedir que o usuário volte à página de login. - Use
ProtectedRoutepara proteger as rotas do painel, redirecionando para a página de login quando o usuário não estiver conectado e redirecionando automaticamente para a página que o usuário pretendia visitar originalmente após um login bem-sucedido. - Use
useSearchParamspara implementar uma página de lista de produtos: ela deve oferecer filtragem por categoria, ordenação por preço e paginação. Os critérios de filtragem devem constar na URL e devem ser mantidos após a atualização da página.