Apresentando o problema e testando uma solução manual
Qual é o problema que queremos resolver agora? Neste momento, nossas páginas estão todas empilhadas. O que gostaríamos que acontecesse? Queremos exibir apenas a página de cadastro quando alguém acessa a URL /cadastro. Queremos que apenas a página de cadastro seja mostrada conforme a URL; ou seja, em /login, /feed, /home ou na raiz do projeto (a página inicial), exibir o feed. É isso que desejamos: não exibir as páginas empilhadas.
Para visualizar o cenário atual, veja como o App está renderizando todas as páginas de forma empilhada:
import Register from './pages/Register'
import Login from './pages/Login'
import Feed from './pages/Feed'
import NewPost from './pages/NewPost'
import Profile from './pages/Profile'
import EditProfile from './pages/EditProfile'
function App() {
return (
<>
<Register />
<Login />
<Feed />
<NewPost />
<Profile />
<EditProfile />
</>
)
}
Podemos abrir nosso App e acessar a propriedade global window.location. Em window.location existem várias propriedades, e uma delas é pathname, que indica qual é a URL, o caminho que estamos acessando. Podemos usar window.location.pathname. Se temos acesso à URL (o que vem depois de localhost:5173, ou seja, o recurso que estamos acessando), podemos ir ao VS Code e escrever um if. Não no index, e sim em nosso app.tsx. Podemos escrever um if mais extenso. Por exemplo, definir const url = window.location.pathname:
import Register from './pages/Register'
import Login from './pages/Login'
import Feed from './pages/Feed'
import NewPost from './pages/NewPost'
import Profile from './pages/Profile'
import EditProfile from './pages/EditProfile'
function App() {
const url = window.location.pathname;
return (
<>
<Register />
<Login />
<Feed />
<NewPost />
<Profile />
<EditProfile />
</>
)
}
Em seguida, verificar com um if se url === '/cadastro' para retornar nossa página de cadastro:
import Register from './pages/Register'
import Login from './pages/Login'
import Feed from './pages/Feed'
import NewPost from './pages/NewPost'
import Profile from './pages/Profile'
import EditProfile from './pages/EditProfile'
function App() {
const url = window.location.pathname;
if (url === '/cadastro') {
return <Register />
}
return (
<>
<Register />
<Login />
<Feed />
<NewPost />
<Profile />
<EditProfile />
</>
)
}
Ao recarregar, percebemos que não há mais aquele loop infinito; apenas a página de cadastro é exibida.
Adotando o react router e configurando o roteamento
Entretanto, imaginemos o caos que isso se tornaria. Como adicionaríamos muitas páginas se estivermos controlando tudo manualmente? Esse é um enfoque imperativo. O React propõe um enfoque declarativo. Em vez de implementarmos tudo manualmente dessa forma, vamos usar uma biblioteca muito conhecida, focada em roteamento em aplicações React: React Router. Ela possui três modos distintos de operação: Framework (estrutura), Data (dados) e Declarativo. No curso, utilizaremos o modo Declarativo. Deixaremos uma atividade para aprofundamento, para que você entenda a diferença entre esses modos e, se desejar, experimente-os ao longo do tempo. Por ora, durante o curso, utilizaremos a forma Declarativa.
Em reactrouter.com, ao acessarmos o site, podemos ir para a página de docs (documentação) por meio de um botão. No menu à esquerda há uma lista extensa de tópicos. Ela começa pelo modo Framework (estrutura), depois apresenta o modo Data (dados) e, ao final, o modo Declarative (declarativo). Logo abaixo de Declarative (declarativo) está Installation (instalação). Lá é indicado que podemos instalar a partir de um modelo de React, se quisermos, ou instalar diretamente no projeto usando o comando npm i react-router. É isso que faremos: instalar com npm i react-router.
Abrimos o terminal do VS Code e realizamos a instalação com:
Em seguida, serão apresentadas algumas regras para que nossa aplicação funcione corretamente. A primeira instrução é: precisamos de um BrowserRouter.
Configuração do BrowserRouter no projeto
A documentação sugere realizar a configuração no arquivo main.tsx. Vamos seguir essa indicação: no projeto, abrimos o main.tsx e, ao redor do App, envolvemos com o BrowserRouter. Primeiro, veja como está o main.tsx antes da alteração:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
Agora, envolvemos o App com o BrowserRouter:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
)
Por fim, garantimos que o BrowserRouter seja importado:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { BrowserRouter } from 'react-router'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
)
A partir do momento em que fazemos essa alteração, isso não deve quebrar a aplicação nem alterar seu comportamento. Fomos ao RunnerCircle e está tudo correto.
Agora temos nossa extensão para observar. Nela, foi colocado um BrowserRouter na parte superior e adicionados vários elementos no meio:
BrowserRouterRouter Navigation ProviderLocation Provider
Tudo isso é do React Router; não precisamos compreender ainda o que cada um faz. Em seguida, a execução entra no nosso App, registra e começa a exibir Auto Layout, Login, Feed, New Post, New Profile e Edit Profile. Também é indicado que algumas páginas estão memorizadas, o que se relaciona ao React Compiler que vimos no curso anterior.
Com isso instalado, o próximo passo é configurar o roteamento, isto é, começar a declarar nossas regras.
Declaração das rotas
Dentro do BrowserRouter, precisamos trazer o elemento Routes (no plural). Ou seja, dentro do BrowserRouter não vamos mais renderizar o App diretamente; vamos utilizar o componente Routes para declarar quais serão nossas rotas, podendo definir várias. Primeiro, adicionamos o contêiner Routes:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { BrowserRouter } from 'react-router'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<Routes>
</Routes>
</BrowserRouter>
</StrictMode>,
)
Copiamos e colamos a sintaxe para facilitar e pedimos ao VS Code que importe o elemento Route (no singular). Inicialmente, declaramos uma rota para '/' apontando para o App:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { BrowserRouter } from 'react-router'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
</Routes>
</BrowserRouter>
</StrictMode>,
)
Em seguida, deixamos os imports corretos para Route e Routes:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { BrowserRouter, Route, Routes } from 'react-router'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
</Routes>
</BrowserRouter>
</StrictMode>,
)
Seguindo a lógica proposta:
- Se alguém acessar a URL
'/', queremos mostrar a página Feed. Importamos Feed e, com isso, não precisamos mais renderizar o App.
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { BrowserRouter, Route, Routes } from 'react-router'
import Feed from './pages/Feed/index.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<Feed />} />
</Routes>
</BrowserRouter>
</StrictMode>,
)
- Também adicionamos a rota para a página
/cadastro, utilizando a página Register. Primeiro, declaramos a rota:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { BrowserRouter, Route, Routes } from 'react-router'
import Feed from './pages/Feed/index.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<Feed />} />
<Route path="/cadastro" element={<Register />} />
</Routes>
</BrowserRouter>
</StrictMode>,
)
E, por fim, garantimos a importação de Register:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import { BrowserRouter, Route, Routes } from 'react-router'
import Feed from './pages/Feed/index.tsx'
import Register from './pages/Register/index.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<Feed />} />
<Route path="/cadastro" element={<Register />} />
</Routes>
</BrowserRouter>
</StrictMode>,
)
Dessa forma, declaramos nosso roteamento:
- Ao acessar a raiz
'/', mostramos Feed. - Ao acessar
/cadastro, mostramos Register.
Convenção de idioma nas URLs
Nosso código-fonte está sempre em inglês, mas as rotas e as URLs ficarão em português para serem mais amigáveis para a pessoa usuária. Embora o código-fonte permaneça em inglês, estamos escrevendo exclusivamente para pessoas falantes de português. Por isso, vemos este 'cadastro' no meio de diversas palavras em inglês.
Testes e verificação
Ao voltar para a aplicação, já estávamos apontando para /cadastro e tudo está correto: a árvore de componentes agora exibe apenas Register. Se acessarmos a URL inicial, a aplicação mostrará Feed. Essa página exibe todos os posts.
Temos, assim, uma navegação básica em funcionamento. Na sequência, vamos continuar explorando o React Router e adicionando mais funcionalidades.
Até a próxima!