ReactJs: Manipulando rotas com react-router

Ao desenvolver uma aplicação, pensamos em quantas páginas existirão, a navegação entre elas e como elas serão desenvolvidas. Para lidar com ReactJs, trouxemos uma forma simples de como fazer essa manipulação utilizando react-router-dom.
Jessica Meira | 22 de julho de 2021

Neste artigo vamos abordar a manipulação de rotas utilizando a biblioteca react-router-dom, algo que será muito útil caso você estiver dando os seus primeiros passos no ReactJS.

Em outros conteúdos aqui no blog, nós já entendemos o que é o ReactJS, sabemos como os componentes funcionam, também entendemos sobre os hooks e até mesmo o conceito de elaboração de testes. Agora vamos considerar que, antes de você chegar nesses assuntos, seja necessário entender um pouco sobre a navegação e, mais precisamente, sobre as rotas no React. Afinal, como isso funciona por aqui? Vou te explicar…

Para que servem as rotas no ReactJS

Pensando que ao utilizar o React para desenvolvimento de uma aplicação padrão SPA (Single Page Application) e que nessa aplicação seja possível acessar outras interfaces e componentes, há a necessidade de fazer o roteamento dos caminhos que serão acessados. A maneira mais completa de fazer isso e também uma das mais utilizadas é com a biblioteca chamada react-router, que possui uma versão web chamada react-router-dom.

Pode ser que você encontre outros projetos em outras bibliotecas, mas aqui nesse post vamos tratar desta. Para iniciar, começamos criando a nossa aplicação com o create-react-app, que você já deve ter se acostumado.

npx create-react-app seuprojeto

O que é react-router-dom

O react-router-dom é uma biblioteca padrão para que você consiga fazer o roteamento das páginas da sua aplicação de forma dinâmica.

A instalação dessa biblioteca é tão fácil quanto qualquer outra. A linha de comando para sua instalação é a:

npm install react-router-dom

Os componentes utilizados até antes da versão 6 eram:

  • BrowserRouter: é a função raiz da lib. Todas as rotas precisam estar declaradas dentro desse escopo, ou seja, nenhuma outra rota poderá ser acessada se não tiver sido declarada aqui. Lembrando que há apenas uma declaração de BrowserRouter no projeto. Como o nome é muito extenso, é normal no import deste componente ser utilizado o as Router.

  • Route: o segundo a ser declarado é o Route. Pode-se dizer que é o mais importante dentro dos componentes do React Router. É de responsabilidade dele a renderização da interface, sendo ela um componente ou uma página, quando o caminho (path) combinar com o esperado. Pode ser declarada várias rotas contendo o path, o componente e também a props se necessário.

  • Switch: esse componente é utilizado a partir do momento que adicionamos várias rotas. Ao declarar o switch, a rota será buscada e, então, quando for encontrada a busca será finalizada, já que sem esse componente todas as rotas serão processadas.

  • Link: basicamente permite que haja navegação de linkagem de uma maneira mais fácil, o que antes era feito com as tag agora pode ser substituído por.

Todos os componentes listados até agora serão apresentados em código.

Como já criamos a nossa aplicação, é possível criar uma pasta dentro de src, no meu caso vou chamar de pages. Como é um exemplo simples de rotas, criei duas páginas ‘home.js’ e ‘about.js’.

Vamos apenas renderizar um texto para exemplo:

import React from 'react';
function Home(){
return (
<div>
<h1>Home</h1>
</div>
)
}
export default Home;

Mesma coisa para o About:

import React from 'react';
function About(){
return (
<div>
<h1>About</h1>
</div>
)
}
export default About;

Nota: É comum que seja criado um arquivo Routes.js na aplicação para lidar com as rotas, mas no caso deste exemplo não será necessário. Utilizarei arquivo App.js:

import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import Home from './pages/home';
import About from './pages/about';
function App() {
return (
<Router>
<div className='App'>
<Link to='/'>Home</Link>
<Link to='/about'>About</Link>
</div>
<Switch>
<Route exact path='/' component={Home} />
<Route path='/about' component={About} />
</Switch>
</Router>
);
}
export default App;

Como a definição destes componentes foi feita, vamos para uma breve explicação da funcionalidade. Começando com o import dos componentes, na função render são passadas as rotas pelo Route, que estão dentro do switch, como esperado, para que não haja o processamento de todas as rotas. Ainda dentro do BrowserRouter foi informado o Link, que servirá como o menu de navegação. Desta forma, não será recarregada a página inteira, apenas o componente.

A cada link clicado, a url será alterada e o componente solicitado será renderizado.

Hooks do react-router

O React Router vem com algumas facilidades que permitem o acesso ao state do Router e navegação dentro do componente:

  • useHistory: permite usar o histórico para navegar;
  • useLocation: permite identificar o location, pathname, state de qualquer componente;
  • useParams: permite ter acesso aos parâmetros de uma maneira mais direta;
  • useRouteMatch: tenta corresponder ao URL atual da mesma maneira que um `````` faria.

Exemplos práticos:

Criando mais uma página chamada user na pasta pages, importando o useParams do react-router-dom, declaramos uma variável chamada nome, que vai receber o parâmetro passado na rota e apresentado na tela :

import React from 'react';
import { useParams } from 'react-router-dom';
function User(){
const {name} = useParams();
return (
<div>
<h1>Logado como {name }</h1>
</div>
)
}
export default User;

Importando o user para no arquivo App.js, criando um link novo e também um route para ele, note que estamos passando o parâmetro :name para a rota:

import React from 'react';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import Home from './pages/home';
import About from './pages/about';
import User from './pages/user';
function App() {
return (
<Router>
<div className='App'>
<Link to='/'>Home</Link>
<Link to='/about'>About</Link>
<Link to='/user/meunome'>User</Link>
</div>
<Switch>
<Route exact path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/user/:name' component={User} />
</Switch>
</Router>
);
}
export default App;

Desta forma, o meunome será apresentado na tela e isso será alterado sempre que for digitado ou passado um novo valor como parâmetro.

Fazendo outra alteração agora para utilizar o useHistory, vou alterar o arquivo about.js, criado inicialmente:

import React from 'react';
import { useHistory, useLocation } from 'react-router-dom';
function About(){
const location = useLocation();
const history = useHistory();
function goBack(){
history.goBack();
}
return (
<div>
<h1>About us</h1>
<h1>Path: {location.pathname}</h1>
<button onClick={goBack}>Back</button>
</div>
)
}
export default About;

Na prática, foi adicionado um botão para retornar na página anterior, utilizando o useHistory, ou seja, importando esse componente do react-router-dom, será declarada uma variável que receberá o useHistory. No clique do botão será chamada a função responsável por lidar com esse retorno goBack, onde o que será invocado é o history.goback(), um dos métodos dentro do useHistory. Além deste, há alguns outros que você pode conferir aqui.

Ainda no arquivo about.js, podemos importar mais um componente, o useLocation, da mesma maneira como foi utilizado o histor. Criamos uma variável que receberá este componente e no render podemos chamar o pathname, por exemplo, como também há outras declarações. Deixarei o link para que você possa entender melhor caso ainda tenha alguma dúvida.

A versão mais utilizada e estável até a publicação deste artigo (julho 2021) é a 5.2.0, porém já podemos falar sobre as modificações da versão 6, lançada em 2020.

Atualizações: React Router 6.0

Algumas alterações foram feitas no último ano, então algumas coisas deixaram de ser necessárias, como a utilização do exact path.

O exact era necessário quando haviam várias rotas e algumas eram parcialmente iguais, desta maneira:

<Switch>
<Route path='/' component={home} />
<Route path='/users' component={User} />
</Switch>

Sempre seria direcionado para a home, pois o primeiro path combinado com a rota solicitada era o ‘/’, assim, o exact era usado para informar ao nosso manipulador de rotas que só poderia renderizar o home caso a rota fosse exatamente igual ao path definido.

Outra facilidade é que o Switch não é mais necessário. Ele ainda é encontrado dentro da lib, mas o indicado agora é o uso do Routes.

Já a respeito da alteração no uso dos Hooks, o useHistory também não é mais necessário e foi substituído por useNavigation.

Outro ponto não abordado anteriormente são as rotas ‘Not Found’, que também é mais direta para definir. Veja:

<Route path='*' element={<h1>Not Found</h1>} />

Na linha estamos apenas renderizando um texto de não encontrado, mas que pode ser alterado também para um componente.

E uma das alterações que foram muito bem recebidas, foi referente ao nested routes.

Como funciona o nested routes?

Agora na versão 6 do react-router-dom é possível ter uma hierarquia de rotas. Simplificando, ao realizar uma compra em um e-commerce, os pedidos feitos, assim como a alteração do seu perfil, ficam vinculados ao usuário. Nisso há a possibilidade de informar quais as rotas filhas para esse componente.

Para entender o funcionamento, dentro do componente será importado o OutLet, que serve como saída, responsável por renderizar um componente filho de uma rota:

import { Outlet } from ‘react-router-dom’

Em seguida será inserido o elemento Outlet, já importando, onde se deseja que a saída seja renderizada: ``````

Para finalizar o que foi explicado até aqui, será utilizado o mesmo exemplo do começo desse artigo, mas instalando a versão 6 do react-router-dom com o comando:

npm i [email protected]

Crie uma pasta chamada ‘pages’ dentro do src e, dentro desta pasta, crie 3 arquivos de nomes ‘home.js’, ‘aboutus.js’ e ‘user.js’

import React from 'react';
export default function Home (){
return(
<h1>Home</h1>
);
}

No arquivo aboutus.js, vamos testar o useNavigate:

import React from 'react';
import { useNavigate } from 'react-router-dom';
export default function About(){
const navigate = useNavigate();
return(
<>
<h1>About</h1>
<button onClick={() => navigate(-1)}>Go back</button>
</>
);
}

O navigate(-1) faz com que ao pressionar o botão de voltar seja redirecionado para a página anterior.

No arquivo App.js, a estrutura ficará da seguinte maneira:

import {BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Home from './pages/home';
import About from './pages/aboutus';
import User from './pages/user';
function App() {
return (
<Router>
<div className='App'>
<ul>
<li> <Link to='/'>Home</Link> </li>
<li> <Link to='/about'>About</Link> </li>
<li> <Link to='/user/meunome'>User</Link> </li>
</ul>
<Routes>
<Route path='/' element={<Home />} />
<Route path='about' element={<About />} />
<Route path='*' element={<h1>Not Found</h1>}/>
<Route path='user/:name' element={<User />} >
<Route path='edit' element={<h1>Editar perfil</h1>}/>
<Route path='Order' element={<h1>Meus Pedidos</h1>}/>
</Route>
</Routes>
</div>
</Router>
);
}
export default App;

As alterações entre ambos os arquivos App.js são:

  • O switch, que neste segundo não é utilizado;
  • Possibilidade de inserir rotas dentro do Route (nested routes);
  • Alteração de component para element;
  • A não utilização do exact path.

E por fim o arquivo user.js:

import React from 'react';
import { useParams, useLocation, Outlet } from 'react-router-dom';
export default function User(){
const {name} = useParams();
const location = useLocation();
return(
<>
<h1>Logado como {name }</h1>
<h3>path: {location.pathname}</h3>
<Outlet />
</>
);
}

Para o user.js, são importados o useParams; para acessar o parâmetro name, o useLocation; e para identificar o caminho atual em que o usuário se encontra o OutLet, que será renderizado logo abaixo das informações. Caso você esteja se perguntando o que vai ser renderizado, lá no arquivo App, na rota /edit e /order estamos passando apenas um <h1> como exemplo. Você pode passar algum outro componente se quiser.

Conclusão

Neste artigo entendemos a usabilidade do react-router-dom para ReactJs, criando rotas, acessando objetos e também parâmetros. A Biblioteca utilizada, além de auxiliar na construção dessa navegação de uma maneira mais objetiva, também é customizável, abrindo um leque de opções de uso que se encaixam na maneira como você vai desenvolver o seu projeto. Espero que tenha sido útil.

Referências:

  • Declarative Routing for React.js
  • Getting Started with React Router
  • react-router-dom
  • React Router v6 Preview
  • React Router v5.1
  • React Router Philosophy
Jessica Meira
Software Engineer | Cursando pós-graduação em Inteligência Artificial, um pouco curiosa nas horas vagas. Mais de 15 anos na área de Tecnologia e apaixonada por música e instrumentos.