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.
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.
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.
Definindo parâmetros com Links
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.
<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.
<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.
// 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()}`
}<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.
// 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
})
}<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.
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.
Próximo: Rotas protegidas, onde um ramo do app pergunta quem você é primeiro.

