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

Parâmetros de busca

Parâmetros de rota dizem ao seu app em qual página você está. Parâmetros de busca, também chamados de query params, descrevem como aquela página deve parecer: qual filtro está ativo, por qual coluna a lista está ordenada, qual página de resultados está sendo exibida. Eles vivem na URL após um ponto de interrogação, como pares chave/valor como /vans?type=rugged, com pares extras ligados por ampersands: /vans?type=rugged&sort=price. Por serem parte da URL, sobrevivem a um refresh e viajam dentro de um link compartilhado, o que os torna um tipo diferente de estado do que qualquer coisa que useState possa guardar.

Estado que pertence à URL

Estado mantido com useState vive na memória. Atualize a página e ele volta ao seu valor inicial; copie a URL para um amigo e ele começa do zero sem nenhuma de suas escolhas. Para muito estado isso é exatamente o certo. Para alguns, é uma perda real: se você filtrou uma lista de vans para apenas as rústicas com um preço máximo, você provavelmente quer que um refresh mantenha aquela visualização, e quer que um link colado abra a mesma lista curada para outra pessoa.

O curso oferece um teste útil: um usuário deveria ser capaz de revisitar ou compartilhar esta página exatamente como está e obter o mesmo resultado? Se sim, considere promover aquele pedaço de estado para fora do React e para a URL como um parâmetro de busca. Filtragem, ordenação e paginação são os candidatos clássicos. A URL então se torna a única fonte de verdade para aquele estado, e seu componente deriva o que renderiza a partir dela, da mesma forma que derivaria de estado ou props.

Lendo parâmetros com useSearchParams

React Router expõe a query string através do hook useSearchParams, e sua forma é deliberadamente próxima a useState: um array contendo o valor atual e um setter.

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

export default function CharacterList() {
  const [searchParams, setSearchParams] = useSearchParams()
  const typeFilter = searchParams.get('type')
  // ...
}

searchParams é uma instância do objeto nativo URLSearchParams do navegador em vez de um objeto simples, então você interage com ele através de seus métodos. .get('type') retorna o valor do parâmetro type como uma string, e retorna null quando aquele parâmetro não está na URL. Aquele null é como seu código sabe que nenhum filtro está ativo. .toString() serializa todo o conjunto de volta para uma query string como type=sith&sort=price, sem o ponto de interrogação no início.

Nota de versão

Os exemplos aqui importam de react-router-dom, o que funciona em v6 e v7; Roteamento cobre o que v7 mudou sobre os pacotes.

Filtrando uma lista a partir de um parâmetro

Com o parâmetro em mãos, filtrar é JavaScript puro no topo do componente. Sem estado, sem efeito: ler um parâmetro e filtrar um array são ambos rápidos, então tudo bem refazer o trabalho em cada render e deixar o resultado sair da URL.

jsx
const typeFilter = searchParams.get('type')

const displayedCharacters = typeFilter
  ? characters.filter(char => char.type.toLowerCase() === typeFilter.toLowerCase())
  : characters

const charEls = displayedCharacters.map(char => (
  <li key={char.name}>{char.name}</li>
))

O ternário trata o caso sem filtro: quando .get retorna null, a lista completa é exibida. Escolher qual array renderizar dessa forma é o mesmo movimento de derivar-ao-renderizar que você usou para renderização condicional, dirigido pela URL em vez de por estado. Cuidado também com diferenças de maiúsculas; os dados armazenam "Sith" enquanto um link editado à mão pode carregar Sith ou sith, então ambos os lados ficam em minúsculas antes de serem comparados.

A forma mais direta de colocar um parâmetro de busca na URL é um Link cujo to começa com um ponto de interrogação. React Router vê o ? no início, mantém você na rota atual, e substitui a query string, o que re-renderiza o componente e re-executa sua filtragem.

jsx
<Link to="?type=jedi">Jedi</Link>
<Link to="?type=sith">Sith</Link>
<Link to=".">Limpar</Link>

Para limpar, to="." navega até o caminho atual sem query anexada; to="" também funciona, e o curso opta pelo ponto como o mais explícito dos dois. Links funcionam melhor quando o filtro é um conjunto fixo de escolhas visíveis: eles renderizam como tags âncora reais, então os usuários podem abrir uma visualização filtrada em uma nova aba ou copiar o endereço antes de clicar.

Definindo parâmetros com a função setter

O segundo elemento de useSearchParams é um setter, e como o de useState ele aceita tanto um valor de substituição quanto um callback. Como um botão não é parte do ecossistema do React Router da forma que Link é, você chama o setter de um manipulador de evento ordinário.

jsx
<button onClick={() => setSearchParams({ type: 'jedi' })}>Jedi</button>
<button onClick={() => setSearchParams({ type: 'sith' })}>Sith</button>
<button onClick={() => setSearchParams({})}>Limpar</button>

O setter é flexível sobre o que aceita: uma string como '?type=jedi' (com ou sem o ponto de interrogação) funciona, mas a forma de objeto mostrada aqui é o que você verá com mais frequência, e um objeto vazio limpa tudo. Recorra ao setter quando os novos parâmetros saem de lógica em vez de um clique em uma escolha fixa: lendo valores de um formulário, respondendo à entrada enquanto o usuário digita, ou definindo vários parâmetros de uma vez.

Substituição limpa os outros parâmetros

Ambas as abordagens até agora hard-codam toda a query string. Tudo bem enquanto type é o único parâmetro que seu app usa, mas no momento em que a URL também carrega algo não relacionado, digamos ?name=jill&type=jedi, clicar em um daqueles links ou botões substitui toda a query string e name=jill se foi. Os botões de limpar são ainda mais drásticos: eles apagam todos os parâmetros na URL, incluindo os que este componente nunca tocou. Se você tem certeza que seu projeto terá apenas um parâmetro, hard-codar está bem. Caso contrário você quer mesclar.

Mesclando com parâmetros existentes

Para Links, a prop to recebe uma string, então o fix é um pequeno helper que executa durante o render: copie os parâmetros atuais para um novo URLSearchParams, mude a chave que está se movendo, e serialize o resultado. Ele vive dentro do componente, já que lê searchParams do hook.

jsx
// dentro de CharacterList, para que possa ler searchParams
function genNewSearchParamString(key, value) {
  const sp = new URLSearchParams(searchParams)
  if (value === null) {
    sp.delete(key)
  } else {
    sp.set(key, value)
  }
  return `?${sp.toString()}`
}
jsx
<Link to={genNewSearchParamString('type', 'jedi')}>Jedi</Link>
<Link to={genNewSearchParamString('type', 'sith')}>Sith</Link>
<Link to={genNewSearchParamString('type', null)}>Limpar</Link>

Isso é JavaScript vanilla em vez de qualquer coisa que React Router oferece. O construtor URLSearchParams aceita felizmente um objeto de parâmetros existente como seu ponto de partida, .set atualiza ou adiciona uma chave, e passar null sinaliza ao helper para .delete a chave em vez disso, então "Limpar" agora remove apenas type e deixa tudo mais na URL intacto.

Para o setter, use seu formulário callback. O callback recebe o objeto de parâmetros anterior, você ajusta a chave, e a retorna.

jsx
// dentro de CharacterList, para que possa chamar setSearchParams
function handleFilterChange(key, value) {
  setSearchParams(prevParams => {
    if (value === null) {
      prevParams.delete(key)
    } else {
      prevParams.set(key, value)
    }
    return prevParams
  })
}
jsx
<button onClick={() => handleFilterChange('type', 'jedi')}>Jedi</button>
<button onClick={() => handleFilterChange('type', null)}>Limpar</button>

Uma surpresa que o curso chama atenção: diferente de um updater de useState, onde mutar o estado anterior é proibido, aqui é ok chamar .delete e .set diretamente em prevParams e retorná-lo. Agora tanto os links quanto os botões mudam apenas o parâmetro que eles possuem.

A mutação ser segura é uma consequência do que o setter realmente faz. Quando você dispõe uma atualização useState, React compara o novo valor com o atual com Object.is antes de agendar um re-render, então retornar o mesmo objeto mutado lê-se como nenhuma mudança e a atualização nunca chega. setSearchParams dispara uma navegação em vez disso: serializa o que você retorna em uma nova localização e faz push, sem nenhuma comparação de identidade para derrotar.

O que o callback recebe

Desde React Router 7.7.0 você obtém uma cópia dos parâmetros atuais; versões anteriores voltando a 6.4, onde o formulário callback chegou, te entregam a instância ao vivo com que o componente está renderizando. Mutar o que você recebe e retorná-lo funciona em qualquer caso.

A analogia useState também para no queueing: duas chamadas setSearchParams no mesmo handler ambas começam da URL atual, então a segunda sobrescreve a primeira. Defina vários parâmetros em uma chamada em vez disso. Cada set, como cada clique de Link, faz push de uma entrada de histórico por padrão, então para parâmetros que mudam a cada digitação passe { replace: true } como o segundo argumento do setter.

Duas arestas mais afiadas aparecem na prática. Primeiro, uma query string pode legalmente repetir uma chave: ?type=jedi&type=sith. .get retorna apenas o primeiro valor, .getAll retorna todos como um array, e .delete(key) remove toda entrada para aquela chave, então os helpers de mescla acima colapsam chaves repetidas em vez de gerenciá-las individualmente. Filtros de multi-seleção precisam de .getAll mais .append, e .delete(key, value) remove um único valor deixando as outras entradas para aquela chave sozinhas.

Segundo, resista a espelhar parâmetros em estado. Copiar searchParams.get('type') em useState via um efeito cria duas fontes de verdade que saem de sincronização para um render; derivar durante render, como cada exemplo aqui faz, mantém a URL como a única autoridade e custa uma recomputação barata.

JunoA URL pode guardar seu estado Algum estado merece sobreviver a um refresh e viajar em um link compartilhado, como qual filtro está ligado. Parâmetros de busca guardam aquele estado na URL após o ponto de interrogação, e useSearchParams permite você lê-lo: searchParams.get('type') retorna o valor, ou null quando o parâmetro não está lá.

Você pode definir parâmetros com um Link para uma query string ou com a função setter, então filtrar sua lista pelo que a URL diz.

JunoA URL pode guardar seu estado Use o teste de compartilhamento: se revisitar o link deve reproduzir a visualização, o estado pertence a um parâmetro de busca.

Leia-o com useSearchParams, derive a lista filtrada no topo do componente, e defina parâmetros com um Link para escolhas visíveis fixas ou o setter para mudanças programáticas.

Hard-codar uma query string completa apaga parâmetros não relacionados, então mescle: construa a string do Link a partir de uma cópia dos parâmetros atuais, ou use o formulário callback do setter e ajuste a chave que você possui.

JunoA URL pode guardar seu estadosetSearchParams é uma navegação em vez de uma atualização de estado, o que é por que mutar o URLSearchParams anterior no callback é seguro e por que cada set faz push de uma entrada de histórico a menos que você passe replace: true.

Lembre que chaves podem repetir, .get lê apenas a primeira e .delete remove todas, e mantenha a URL como a autoridade única derivando durante render em vez de espelhar parâmetros em estado.

Próximo: Rotas protegidas, onde um ramo do app pergunta quem você é primeiro.