Skip to content
This page has been auto-translated and may contain errors.View in English

Parâmetros de rota e localização

Uma página de lista e uma página de detalhe formam o par clássico em um app com rotas: /vans mostra todas as vans, e clicar em uma deve abrir /vans/2 com os detalhes dessa van. Escrever uma rota separada para cada van significaria mexer no roteador cada vez que os dados mudassem. Parâmetros de rota resolvem isso com uma única definição de rota que captura todos: um espaço reservado no caminho guarda o valor que aparecer ali, e a página lê esse valor para buscar os dados certos. Este capítulo cobre esse padrão, mais um truque relacionado: passar informações extras junto com uma navegação, para que uma página de detalhe lembre de coisas como qual filtro a lista tinha aplicado.

Segmentos dinâmicos e useParams

Um segmento dinâmico é uma seção do caminho que começa com dois-pontos. Em vez de capturar texto literal, ele captura qualquer coisa naquela posição e guarda o valor sob o nome que você escolheu:

jsx
<Route path="/vans/:id" element={<VanDetail />} />

Agora /vans/1, /vans/42 e /vans/qualquercoisa renderizam VanDetail. Os dois-pontos marcam id como uma variável dentro do caminho. Pense nisto como um parâmetro de função: a definição da rota é escrita uma vez, e a URL fornece o argumento. A página de lista linka cada item para sua própria URL com o componente Link de Routing, normalmente interpolando o id enquanto percorre os dados:

jsx
{vans.map(van => (
  <Link key={van.id} to={`/vans/${van.id}`}>
    <h3>{van.name}</h3>
  </Link>
))}

Do outro lado, o hook useParams retorna um objeto com uma propriedade por segmento dinâmico no caminho capturado, com chaves pelo nome após os dois-pontos:

jsx
import { useParams } from 'react-router-dom'

export default function VanDetail() {
  const params = useParams()
  // em /vans/2 → { id: "2" }
  return <h1>Van #{params.id}</h1>
}

Um caminho pode conter vários segmentos, como /vans/:id/:type, e cada um aparece como sua própria chave. Um parâmetro capturado é sempre uma string, mesmo quando parece um número, porque vem direto da URL.

Buscando dados para o parâmetro

O parâmetro é normalmente a chave para uma busca. A página de detalhe pega o id da URL e requisita aquele registro, seguindo o padrão baseado em efeitos que Fetching data cobre em profundidade:

jsx
import { useState, useEffect } from 'react'
import { useParams } from 'react-router-dom'

export default function VanDetail() {
  const { id } = useParams()
  const [van, setVan] = useState(null)
  const [error, setError] = useState(null)

  useEffect(() => {
    let active = true
    setVan(null)
    setError(null)
    fetch(`/api/vans/${id}`)
      .then(res => {
        if (!res.ok) throw new Error(`Request failed: ${res.status}`)
        return res.json()
      })
      .then(data => { if (active) setVan(data.vans) })
      .catch(err => { if (active) setError(err) })
    return () => { active = false }
  }, [id])

  if (error) return <h2>Desculpe, não conseguimos carregar essa van.</h2>
  return van ? <h1>{van.name}</h1> : <h2>Carregando...</h2>
}

O array de dependências mantém id em vez de ficar vazio. Se o app alguma vez linkiar de uma página de detalhe para outra, digamos uma seção de "vans similares", React Router troca a URL e o parâmetro sem desmontar o componente. Um array vazio pularia a nova busca e deixaria a van antiga na tela. Depender de id reroda o efeito, e limpar van no topo da execução é o que coloca "Carregando..." de volta na tela: sem isso, o nome da van 2 continua renderizando por todo o tempo que a requisição da van 5 leva.

A verificação res.ok, o catch e o flag de limpeza active são as mesmas proteções que Fetching data argumenta favor de, e uma página acionada por parâmetro precisa delas mais que a maioria: o id chega de uma barra de endereços que qualquer um pode editar, e clicar rapidamente através de páginas de detalhe é como uma resposta para a van que você já saiu chega depois da que você está vendo.

Parâmetros carregam dados da URL para uma página. Às vezes uma página também quer saber algo sobre de onde o visitante veio. No projeto VanLife do curso, a página de lista pode ser filtrada para, digamos, apenas vans de luxo. Clique em uma, aperte "Voltar para todas as vans", e um link simples de volta pousa na lista sem filtro: o filtro se perde, o que fica chato rápido quando vários filtros estão envolvidos.

Uma solução seria copiar a query string para a URL da página de detalhe, e Search params cobre quando estado transportado pela URL assim é a chamada certa: sobrevive compartilhar o link com alguém. Quando a informação é uma gentileza de UX para o visitante atual, React Router oferece um canal mais leve. Link aceita uma prop state, e o que você passa viaja com a navegação sem aparecer na URL:

jsx
<Link
  to={`/vans/${van.id}`}
  state={{ search: `?${searchParams.toString()}`, type: typeFilter }}
>
  <h3>{van.name}</h3>
</Link>

searchParams e typeFilter são os próprios valores useSearchParams da página de lista, do hook que Search params cobre em profundidade a seguir, então o link carrega a query string completa mais o nome do filtro atual. Qualquer valor serializável funciona, embora um objeto com propriedades nomeadas leia melhor.

Lendo com useLocation

A página de destino lê aquele state com o hook useLocation, que retorna um objeto descrevendo a localização atual: pathname, search (a própria query string da URL atual), e state, que guarda o que o Link de entrada passou:

jsx
import { Link, useLocation } from 'react-router-dom'

export default function VanDetail() {
  const location = useLocation()
  const search = location.state?.search || ''
  const type = location.state?.type || 'all'

  return (
    <Link to={`..${search}`} relative="path">
      &larr; Voltar para vans {type}
    </Link>
  )
}

Os dois fallbacks são a parte importante. Alguém que chega em /vans/2 diretamente, de um marcador, um link compartilhado, ou uma URL digitada, chega com location.state definido como null porque nenhum Link o enviou. Ler location.state.search então lançaria um erro, então o encadeamento opcional mais um fallback mantém a página funcionando: o link de volta vai para a lista simples e o texto lê "Voltar para todas as vans". Com state presente, o link restaura a query string exata e o texto fica "Voltar para vans de luxo" ou o que quer que o filtro fosse.

A prop relative="path" faz .. subir um segmento de URL em vez de um nível na árvore de rotas, o que importa quando a rota de detalhe tem um pai diferente do que sua URL sugere. Nested routes explica essa distinção.

O caminho triste em uma página acionada por parâmetro

O curso enquadra tratamento de erros como caminho feliz versus caminho triste: o caminho feliz assume que cada requisição sucede, o caminho triste se prepara para as que não. A própria mecânica de carregamento e erro pertence a Fetching data: carregamento derivado do resultado e do erro ainda estando vazios, um state error definido no catch, e retornos antecipados que renderizam feedback em vez de quebrar em dados ausentes.

O que este capítulo adiciona é que páginas acionadas por parâmetro multiplicam os caminhos tristes. A URL é entrada editável pelo usuário, então /vans/999 é um pressionamento de tecla de distância e o servidor responderá com um 404 para um id que não existe; a verificação res.ok acima é o que transforma aquela resposta em error state e uma mensagem em vez de um objeto em forma de van. E como coberto acima, o state de navegação pode estar ausente. Uma página que alcança a rede através de um parâmetro deve assumir que o parâmetro pode estar errado, a requisição pode falhar, e o state pode ser null, e renderizar algo sensato em cada caso.

Nota de versão

Antes do React Router v5.1, informações de rota chegavam apenas como props: componentes liam props.match.params e props.location. A versão 5.1 adicionou os hooks useParams e useLocation, e a versão 6 removeu a API baseada em props, deixando os hooks como a única forma de entrar. Qualquer componente sob o roteador pode chamá-los, porém profundamente renderize.

Link state é um fino envoltório sobre a History API do navegador. Cada entrada de histórico pode carregar um valor state via history.pushState, e React Router armazena sua prop state ali quando a navegação acontece. Esse mecanismo explica como se comporta: sob BrowserRouter o valor sobrevive a um refresh de página e aos botões de volta e frente, porque vive na entrada de histórico em si e o navegador persiste essas. Desaparece quando a URL viaja, porque nada sobre ele é codificado no endereço: cole o link em outro navegador e location.state é null.

Isso oferece uma regra de decisão limpa para onde dados por-navegação pertencem. Qualquer coisa que deveria sobreviver compartilhamento, marcação, ou uma visita fresca vai na URL como um search param. Qualquer coisa que é uma cortesia para a sessão do visitante atual, como restaurar um filtro atrás de um botão de volta, se encaixa no history state. De qualquer forma, trate location.state como não confiável e possivelmente null em cada leitura; o caso de landing direto é uma certeza em escala.

Parâmetros merecem o mesmo ceticismo mais duas notas mecânicas. Primeiro, um segmento que capturou chega como string, enquanto um segmento opcional como /vans/:id? que não capturou nada chega como undefined (um splat que não capturou nada dá uma string vazia em vez disso), e é por isso que o tipo TypeScript é string | undefined. Uma string id comparada com === contra um número falha silenciosamente, então converta no limite: Number(id) === van.id.

Segundo, uma mudança de parâmetro rerenderiza o componente montado em vez de remontá-lo, e é por isso que o efeito de busca deve listar o parâmetro em suas dependências, e por que qualquer state derivado inicializado de um parâmetro precisa ser resetado quando muda.

O roteador também captura segmentos estáticos antes de dinâmicos, então /vans/new pode coexistir com /vans/:id: React Router classifica a especificidade da rota em vez de tomar ordem de definição, o que mantém uma rota literal de ser engolida por uma vizinha de parâmetro.

JunoUma rota, muitas páginas Dois-pontos em um caminho de rota, como /vans/:id, criam um espaço que qualquer valor pode preencher, então uma rota serve uma página de detalhe para cada item na sua lista. Dentro da página, useParams te passa aquele valor para você buscar os dados certos.

Links também podem carregar um pequeno pacote de informação extra através de sua prop state, e useLocation lê do outro lado. Lembre que o pacote pode estar ausente se alguém chegou de um marcador, então sempre tenha um fallback pronto.

JunoUma rota, muitas páginas Defina path="/vans/:id" uma vez, leia o id com useParams, e coloque no array de dependências do seu efeito de busca para que navegar entre páginas de detalhe refaça a busca.

Para manter um filtro vivo atrás de um botão de volta, passe a query string na prop state do Link e leia via location.state com encadeamento opcional e um fallback, porque landings diretos chegam com state como null.

Trate carregamento, erros e ids ruins do mesmo jeito que qualquer busca, com a URL tratada como entrada do usuário.

JunoUma rota, muitas páginas Link state viaja na entrada de histórico através de pushState, então sobrevive refresh e navegação de volta mas nunca cruza navegadores ou URLs compartilhadas; coloque dados compartilháveis em search params e reserve history state para cortesias locais à sessão.

Um parâmetro capturado é uma string, e uma mudança de parâmetro rerenderiza sem remontar, então efeitos devem depender dele. Captura de rota classifica especificidade, deixando segmentos estáticos como /vans/new coexistirem seguramente com /vans/:id.

Próximo: Search params, onde filtros e ordens de classificação vivem na URL.