Artigo original: https://www.freecodecamp.org/news/a-complete-beginners-guide-to-react-router-include-router-hooks/

Nota do tradutor: A versão 6 do react-router, sua versão mais recente, trouxe algumas modificações importantes ao comportamento da biblioteca, sendo possível que algumas das ações sugeridas aqui não produzam os resultados esperados caso ela seja a versão instalada em seu computador. Sugerimos realizar os seus testes com a versão 5 da biblioteca.

React é uma biblioteca JavaScript para construir interfaces de usuário. Também podemos estendê-la para criar aplicações de várias páginas com a ajuda do React Router. Ela é uma biblioteca de terceiros que permite o roteamento interno em nossas aplicações em React.

Neste tutorial, abordaremos tudo o que você precisa saber para começar a usar React Router.

Configurando o projeto

Para poder acompanhar, você precisará criar uma aplicação em React executando o seguinte comando em seu terminal:

npx create-react-app react-router-guide

Depois, adicione estas linhas de código ao arquivo App.js:

import React from "react";
import "./index.css"

export default function App() {
  return (
    <main>
      <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/about">About</a></li>
          <li><a href="/contact">Contact</a></li>
        </ul>
        </nav>
     </main>
  );
}
// Home Page
const Home = () => (
  <Fragment>
    <h1>Home</h1>
    <FakeText />
  </Fragment>
  );
// About Page
const About = () => (
  <Fragment>
    <h1>About</h1>
    <FakeText />
  </Fragment>
  );
// Contact Page
const Contact = () => (
  <Fragment>
    <h1>Contact</h1>
    <FakeText />
  </Fragment>
  );

const FakeText = () => (
  <p>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </p>
  )

Então, se você estiver pronto para começar, vamos começar respondendo a uma pergunta importante: o que é roteamento?

O que é roteamento?

Roteamento é a capacidade de mostrar diferentes páginas ao usuário. Isso significa que o usuário pode mover-se entre diferentes partes de um aplicativo inserindo um URL ou clicando em um elemento.

Como você já deve saber, por padrão, o React vem sem roteamento, pois foi pensado no modelo de aplicação single page (ou "de página única", em português), cuja funcionalidade está concentrada em uma única página. Porém, para tornar isso possível em nosso projeto, precisamos adicionar uma biblioteca chamada react-router (texto em inglês).

Para instalá-la no projeto, execute o seguinte comando no seu terminal (caso você utilize o yarn como gerenciador de pacotes):

yarn add react-router-dom
Nota do tradutor: para instalar a versão 5 do react-router, use yarn add react-router-dom@5.3.3

Se você utiliza o npm, digite:

npm install react-router-dom
Nota do tradutor: para instalar a versão 5 do react-router, use npm install react-router-dom@5.3.3

Agora que instalamos nosso roteador com sucesso, vamos começar a usá-lo na próxima seção.

Configurando o roteador

Para habilitar o roteamento em nossa aplicação em React, primeiro, precisamos importar o BrowserRouter do react-router-dom.

No arquivo App.js, digite o seguinte:

import React, { Fragment } from "react";
import "./index.css"

import { BrowserRouter as Router } from "react-router-dom";

export default function App() {
  return (
  <Router>
    <main>
      <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/about">About</a></li>
          <li><a href="/contact">Contact</a></li>
        </ul>
      </nav>
    </main>
</Router>
  );
}

Isso deve permear tudo na nossa aplicação, sempre que o roteamento for necessário. Isso significa que, se precisarmos rotear qualquer parte da nossa aplicação, devemos sempre envolver o componente de nível superior (é o componente que o encapsula) com o BrowserRouter.

A propósito, você não precisa renomear BrowserRouter as Router como eu faço. Faço desse modo pois prefiro manter as coisas mais legíveis.

Como um roteador apenas não faz muito sozinho, vamos adicionar uma rota na próxima seção.

Rotas de renderização

Para renderizar as rotas, temos que importar o componente Route do nosso Router.

No arquivo App.js, adicione o seguinte código:

import React, { Fragment } from "react";
import "./index.css"

import { BrowserRouter as Router, Route } from "react-router-dom";

export default function App() {
  return (
  <Router>
    <main>
      <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/about">About</a></li>
          <li><a href="/contact">Contact</a></li>
        </ul>
      </nav>
  <Route path="/" render={() => <h1>Welcome!</h1>} />
    </main>
</Router>
  );
}

Depois, adicione-o onde quiser renderizar o conteúdo. O componente Route tem várias propriedades. Por agora, precisaremos somente do path e do render.

path: é o caminho da rota. Aqui, usamos a rota / para definir o caminho da página inicial.

render: exibirá o conteúdo sempre que a rota for solicitada. No nosso caso, renderizaremos uma mensagem de boas-vindas ao usuário.

Em alguns casos, definir rotas como essa é bastante tranquilo. Imagine, no entanto, um caso em que tenhamos que lidar com um componente real – usar o render nesse caso pode não ser a solução ideal.

Então, como podemos exibir um componente real? Bem, o componente Route tem outra propriedade chamada component.

Vamos atualizar um pouco nosso exemplo para ver como ele funciona.

No App.js, adicione isto:

import React, { Fragment } from "react";
import "./index.css"

import { BrowserRouter as Router, Route } from "react-router-dom";

export default function App() {
  return (
   <Router>
    <main>
      <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/about">About</a></li>
          <li><a href="/contact">Contact</a></li>
        </ul>
      </nav>

    <Route path="/" component={Home} />
    </main>
</Router>
  );
}

const Home = () => (
  <Fragment>
    <h1>Home</h1>
    <FakeText />
  </Fragment>
  );

Agora, em vez de renderizar uma mensagem, nossa rota carregará o componente Home.

Porém, para utilizarmos todo o poder do React Router, precisamos ter várias páginas e links para brincar. Como já temos páginas (componentes, se você preferir), vamos adicionar agora alguns links para podermos alternar entre elas.

Para adicionar links ao nosso projeto, usaremos novamente o React Router.

Em seu arquivo App.js, adicione este código:

import React, { Fragment } from "react";
import "./index.css"

import { BrowserRouter as Router, Route, Link } from "react-router-dom";

export default function App() {
  return (
   <Router>
    <main>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>
      </nav>

    <Route path="/" exact component={Home} />
    <Route path="/about"  component={About} />
    <Route path="/contact"  component={Contact} />

    </main>
</Router>
  );
}

const Home = () => (
  <Fragment>
    <h1>Home</h1>
    <FakeText />
  </Fragment>
  );

const About = () => (
  <Fragment>
    <h1>About</h1>
    <FakeText />
  </Fragment>
  );

const Contact = () => (
  <Fragment>
    <h1>Contact</h1>
    <FakeText />
  </Fragment>
  );

Depois de importar o componente Link, temos que atualizar um pouco nossa barra de navegação. Agora, ao invés de usarmos a tag de link a com o parâmetro href, o React Router usa o Link com o parâmetro to para poder alternar entre as páginas sem recarregá-las.

Então, vamos adicionar duas novas rotas, a página About e a Contact, para poder alternar entre as páginas (ou componentes).

Feito isso, podemos acessar partes diferentes do nosso aplicativo por meio de links. Há, no entanto, um problema com nosso roteador do jeito que está: o componente Home está sendo exibido sempre, mesmo se navegarmos para outras páginas.

Isso acontece porque o React Router verifica se o path definido começa com /. Em caso positivo, ele renderiza o componente. Como nossa primeira rota é somente /, o componente Home é renderizado sempre.

No entanto, conseguimos alterar esse comportamento padrão adicionando a propriedade exact ao componente Route.

No App.js, adicione:

    <Route path="/" exact component={Home} />

Agora, ao atualizar a rota Home com a propriedade exact, ela é renderizada apenas se corresponder ao caminho completo.

Ainda podemos aprimorar envolvendo nossas rotas Switch, dizendo ao React Router para carregar apenas uma rota por vez.

No App.js, adicione:

import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";

  <Switch>
    <Route path="/" exact component={Home} />
    <Route path="/about"  component={About} />
    <Route path="/contact"  component={Contact} />
  </Switch>

Bom, agora que temos novos links, vamos usá-los para passar parâmetros.

Passando parâmetros de rota

Para passar dados entre páginas, teremos que atualizar nosso código.

Adicione o seguinte código ao arquivo App.js:

import React, { Fragment } from "react";
import "./index.css"

import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";

export default function App() {
  const name = 'John Doe'
  return (
   <Router>
    <main>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to={`/about/${name}`}>About</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>
      </nav>
    <Switch>
      <Route path="/" exact component={Home} />
      <Route path="/about/:name"  component={About} />
      <Route path="/contact"  component={Contact} />
    </Switch>
    </main>
</Router>
  );
}

const Home = () => (
  <Fragment>
    <h1>Home</h1>
    <FakeText />
  </Fragment>
  );

const About = ({match:{params:{name}}}) => (
  // props.match.params.name
  <Fragment>
    <h1>About {name}</h1>
    <FakeText />
  </Fragment>
);

const Contact = () => (
  <Fragment>
    <h1>Contact</h1>
    <FakeText />
  </Fragment>
  );

Como você pôde ver acima, começamos declarando uma nova constante name, que será passada como parâmetro para o componente About. Então, anexamos name ao link correspondente.

Por conta disso, agora temos que atualizar a rota do componente About, ajustando seu caminho para receber name como parâmetro. Fazemos isso dessa maneira: path="/about/:name".

Com o parâmetro sendo recebido como props do componente About, a única coisa que temos a fazer é desestruturar as propriedades e depois recuperar a propriedade name. Aliás, para sua informação, {match:{params:{name}}} é o mesmo que props.match.params.name.

Há situações, contudo, em que não é desejável usar links para navegar entre as páginas. Vamos ver quais são essas situações abaixo.

Às vezes, precisaremos esperar que uma operação termine para, somente então, navegar para a próxima página. Vamos lidar com esse caso na próxima seção.

As props que recebemos possuem alguns métodos convenientes que podemos usar para navegar entre as páginas.

No App.js, adicione o seguinte:

const Contact = ({history}) => (
  <Fragment>
    <h1>Contact</h1>
    <button onClick={() => history.push('/') } >Go to home</button>
    <FakeText />
  </Fragment>
  );

Aqui, estamos puxando o objeto history das props que recebemos. Ele tem alguns métodos úteis como o goBack, goForward e outros. Mas aqui, usaremos o método push para podermos ir para a página inicial.

Então, vamos lidar com o caso em que queremos redirecionar nosso usuário após uma ação.

Redirecionando para outra página

O React Router tem outro componente chamado Redirect. Como você já deve ter adivinhado, ele nos ajuda a redirecionar o usuário para outra página.

No App.js, adicione o seguinte código:

import { BrowserRouter as Router, Route, Link, Switch, Redirect } from "react-router-dom";

const About = ({match:{params:{name}}}) => (
  // props.match.params.name
  <Fragment>
    { name !== 'John Doe' ? <Redirect to="/" /> : null }
    <h1>About {name}</h1>
    <FakeText />
  </Fragment>
);

Agora, se o parâmetro name passado não for igual a John Doe, o usuário será redirecionado automaticamente para a página inicial.

Você pode argumentar que devemos redirecionar o usuário com props.history.push('/). Bem, o componente Redirect substitui a página, fazendo com que o usuário não possa voltar para a página anterior. Agora, no entanto, com o método push, isso é possível, mas você pode usar props.history.replace('/) para imitar o comportamento do Redirect.

Vamos seguir em frente e lidar com o caso em que o usuário acessa uma rota que não existe.

Redirecionando para uma página 404

Para redirecionar o usuário para uma página 404, você pode criar um componente para mostrar. Para simplificar, porém, apenas exibirei uma mensagem com o render.

import React, { Fragment } from "react";
import "./index.css"

import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";

export default function App() {
  const name = 'John Doe'
  
  return (
   <Router>
    <main>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to={`/about/${name}`}>About</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>
      </nav>
    <Switch>
      <Route path="/" exact component={Home} />
      <Route path="/about/:name"  component={About} />
      <Route path="/contact"  component={Contact} />
      <Route render={() => <h1>404: page not found</h1>} />
      
    </Switch>
    </main>
</Router>
  );
}

A nova rota que adicionamos vai capturar todas as rotas que não existem e redirecionar o usuário para a página 404.

Veremos como proteger nossas rotas na próxima seção.

Protegendo rotas

Embora existam muitas formas de proteger rotas no React, aqui, vou apenas verificar se o usuário está autenticado e redirecioná-lo para a página apropriada.

import React, { Fragment } from "react";
import "./index.css"

import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";

export default function App() {
  const name = 'John Doe'
  const isAuthenticated = false
  return (
   <Router>
    <main>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to={`/about/${name}`}>About</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>
      </nav>
    <Switch>
      <Route path="/" exact component={Home} />
      {
      isAuthenticated ? 
      <>
      <Route path="/about/:name"  component={About} />
      <Route path="/contact"  component={Contact} />
      </> : <Redirect to="/" />
      }
      
    </Switch>
    </main>
</Router>
  );
}

Como você pode ver, declaramos manualmente uma variável imitando uma autenticação, pois esse assunto é extenso e foge do escopo deste artigo.

Em seguida, verificamos se o usuário está autenticado ou não. Se estiver, renderizamos as páginas protegidas. Caso contrário, redirecionamos para a página inicial.

Abordamos muita coisa até este ponto, mas uma parte interessante ainda não foi explicada: os hooks de roteamento.

Vamos falar deles nesta seção final e apresentar os Hooks.

Router Hooks

Router Hooks (ou "ganchos" de roteamento) tornam as coisas muito mais fáceis, pois você pode acessar o histórico, a localização ou as propriedades de modo fácil e elegante. Vejamos alguns exemplos:

useHistory

O hook useHistory nos permite acessar à instância do histórico sem puxá-la das props.

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

const Contact = () => {
const history = useHistory();
return (
  <Fragment>
    <h1>Contact</h1>
    <button onClick={() => history.push('/') } >Go to home</button>
  </Fragment>
  )
  };

useParams

Esse hook nos ajuda a obter o parâmetro passado no URL sem usar o objeto props.

import { BrowserRouter as Router, Route, Link, Switch, useParams } from "react-router-dom";

export default function App() {
  const name = 'John Doe'
  return (
   <Router>
    <main>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to={`/about/${name}`}>About</Link></li>
        </ul>
      </nav>
    <Switch>
      <Route path="/" exact component={Home} />
      <Route path="/about/:name"  component={About} />
    </Switch>
    </main>
</Router>
  );
}

const About = () => {
  const { name } = useParams()
  return (
  // props.match.params.name
  <Fragment>
    { name !== 'John Doe' ? <Redirect to="/" /> : null }
    <h1>About {name}</h1>
    <Route component={Contact} />
  </Fragment>
)
};

useLocation

Este hook retorna o objeto location, que representa o URL atual.

import { useLocation } from "react-router-dom";

const Contact = () => {
const { pathname } = useLocation();

return (
  <Fragment>
    <h1>Contact</h1>
    <p>Current URL: {pathname}</p>
  </Fragment>
  )
  };

Considerações finais

Espero que você tenha gostado deste tutorial, pois React Router é realmente uma biblioteca incrível, que nos ajuda a transformar nossa aplicação de página única, trazendo a sensação de que ela é uma aplicação com várias páginas, além de ter uma ótima usabilidade (basta ter em mente - no final das contas, que ainda estamos falando de uma aplicação de página única).

Agora, com os hooks de roteamento, você pode ver a facilidade e a elegância deles aplicada. Eles são, definitivamente, algo a considerar em seu próximo projeto.

Você também pode ler mais artigos meus (em inglês) no meu blog.

Próximo passo

Documentação do React Router (texto em inglês).

Foto produzida por Joshua Sortino e extraída do Unsplash