Existem muitos tutoriais espalhados pela internet que estão desatualizados ou até mesmo incorretos.
Nos últimos anos o React Router passou por muitas mudanças e possivelmente foi isso que acarretou na existência desses conteúdos desatualizados.
O objetivo deste post é justamente detalhar as abordagens corretas de se fazer a navegação via programação.
Sempre que você usar o React Router em sua aplicação, uma hora ou outra você precisará navegar entre as rotas de maneira programática.
O que eu adoro no React Router é sua dedicação ao código declarativo “React like”. Desde a versão 4 o React Router é verdadeiramente um roteador React.
O objetivo principal da sua reformulação foi alinhar a visão do React Router com a do React. Fundamentalmente, o que isso significa é que os mesmos princípios e processos de pensamento que se aplicam ao React também devem se aplicar ao Roteador.
Se dividirmos o React em três princípios básicos, teremos composição de componente, interface do usuário declarativa e gerenciamento de estado, especificamente evento do usuário -> alteração de estado -> nova renderização
.
Como a visão do React Router está alinhada com a do React, a navegação programática com o React Router deve, por definição, seguir esses três conceitos principais. Então, não se espante com o que está por vir.
A primeira abordagem para fazer navegação programática usando React Router v4+ usa o componente <Redirect />
. Não tenha um “piripaque”!
<Redirect />
Vejamos um exemplo e veremos mais sobre por que isso não é tão louco quanto parece à primeira vista.
Um caso de uso comum é, redirecionar para o a tela de login após perceber que o token de autenticação não mais existe.
import React, {useState, useEffect} from 'react';
import {Redirect} from 'react-router-dom';
function Dashboard() {
const [toLogin, setToLogin] = useState(false);
useEffect(() => {
const token = localStorage.getItem('appToken');
if(!token) {
setToLogin(true);
}
},[]);
if (toLogin) {
return <Redirect to='/login' />
}
return (
<div>
<h1>Dashboard</h1>
...
</div>
);
}
Depois de ler isso, há pelo menos uma pequena chance de você odiar. Em vez de usar uma API imperativa (history.push), estamos usando um componente de redirecionamento declarativo. Novamente, a razão para isso é porque ela se alinha exatamente aos princípios do próprio React.
<Redirect /> é:
- Compositivo (ok)
- Declarativo (ok)
evento do usuário -> alteração de estado -> nova renderização
(ok)
–Tá! Mas e as desvantagens?
A crítica mais ouvida é que você precisa criar uma nova propriedade no estado do componente para saber quando renderizar o redirecionamento. Isso é válido, mas, novamente, esse é basicamente o ponto principal de React – as alterações de estado atualizam a interface do usuário. “Mais código pra se digitar!?”, sim.
Naturalmente, ao definir e modificar explicitamente seu estado, você deve digitar mais. Meu argumento é que o estado explícito que leva a uma API declarativa é melhor do que o estado implícito tratado por uma API imperativa.
history.push()
A segunda abordagem usa a biblioteca History. Por baixo dos panos ela é a engrenagem principal do React Router. Quando um componente é renderizado pela rota (Route), ele recebe três props diferentes: location, match e history.
A propriedade history
vem da biblioteca History e é acompanhada de várias propriedades interessantes relacionadas ao roteamento. No nosso caso history.push()
é o que nos interessa. Esse método insere uma nova entrada na pilha de histórico, em outras palavras, redireciona o usuário para uma nova rota.
function Dashboard({history}) {
useEffect(() => {
const token = localStorage.getItem('appToken');
if (!token) {
history.push('/login');
}
},[]);
return (
<div>
<h1>Dashboard</h1>
...
</div>
);
}
Tem menos linhas! É Pior… mas com menos linhas! =)
Vale ressaltar que se o componente que necessita fazer a navegação programática não estiver sendo renderizado pelo React Router, a prop history
não existirá em seu contexto.
–Mas e aí? Como faço para usar o history nesse caso?
Pra isso serve o Higher Order Component (HOC) withRouter
.
Ele vai fornecer para o componente que o utilizar a prop history
.
import {withRouter} from 'react-router-dom';
function Dashboard({history}) {
useEffect(() => {
const token = localStorage.getItem('appToken');
if (!token) {
history.push('/login');
}
},[]);
return (
<div>
<h1>Dashboard</h1>
...
</div>
);
}
export default withRouter(Dashboard);
É isso pessoal, vimos como fazer navegação programática com o React Router usando <Redirect />
e history.push
. Qual deles você deve usar? Isso vai depender de você e cada caso. Eu particularmente incentivo o uso do <Redirect />
.
Críticas e sugestões são sempre bem vindas.
Até mais!