Navegação programática com o React Router

Compartilhe nas redes sociais

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!