Executando verificação de segurança...
3

URL States: guardando dados do usuário na URL em NextJS (App Dir)

É muito comum em frameworks como React guardar o estado do usuário em hooks como useState. E isso é ótimo, pois é simples e rápido. Porém, às vezes, é interessante permitir ao usuário manter esse usuário salvo e compartilhável, isto é, permitir que ele possa salvar o que ele estava fazendo e até mesmo compartilhar isso com outros usuários.

O interessante de manter o estado salvo na URL é que é possível fazer tudo isso. Um exemplo é quando você quer mostrar uma pesquisa no Google para algum amigo: basta copiar a URL e ele verá o mesmo que você (ou deveria…).

Vale destacar que o uso de URL States não deve ser generalizado - pelo contrário, deve ser usado somente quando seu uso se justifica. Ou seja, se aquele estado é algo que faz sentido ser compartilhado. Um exemplo de uso são query de pesquisas e filtros em dashboards.

Criando um hook

Antes de mais anda, vale deixar claro que você não precisa de um hook para lidar com isso. Porém, eu resolvi criar um para me ajudar. Se você quiser, pode copiar o código e usar onde quiser.
O código do hook foi baseado na solução de commerce da vercel. Na versão deles, quando o usuário pesquisa algo, a informação é tratada como um estado de URL.

"use client";

import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";

export default function useQueryState(key: string, defaultValue: string = "") {
  const router = useRouter();
  const searchParams = useSearchParams();
  const pathname = usePathname();

  const [value, setValue] = useState(searchParams.get(key) ?? defaultValue);

  useEffect(() => {
    const newParams = new URLSearchParams(searchParams.toString());

    newParams.set(key, value);

    if (!value) newParams.delete(key);

    router.replace(`${pathname}?${newParams.toString()}`);
  }, [key, pathname, router, searchParams, value]);

  return [value, setValue] as const;
}

O código acima funciona basicamente como um useState, porém com algumas diferenças. Ele recebe uma key e um valor inicial. A key é como ele irá aparecer na URL, por exemplo, se sua key for “query”, então sua URL será “seusite.com/blog?query=valor”.

Um exemplo de como usar:

// define os estado individualmente
// ambos irão aparecer na url
const [search, setSearch] = useQueryState("search");
const [tag, setTag] = useQueryState("tag");

return (
    <input
        type="text"
        placeholder="Ex: abacaxi"
        defaultValue={search}
        onChange={(event) => {
            setSearch(event.target.value);
        }}
      />
)

Se quiser ver um exemplo de como ele funciona, pode usar o campo de pesquisa e filtro do meu blog.

Um obrigado ao comentário do EdsonFerreira pelas dicas/sugestões!

Carregando publicação patrocinada...
2

O conceito de guardar o estado do usuário na URL é interessante, especialmente para situações onde o estado precisa ser compartilhado ou persistido entre sessões. O código apresentado é uma abordagem simples e direta para alcançar esse objetivo no contexto do Next.js. No entanto, há algumas considerações e melhorias que podem ser feitas:

  1. Generalização do Hook: O hook useQueryState está hardcoded para a rota /blog. Seria mais útil se ele pudesse ser usado em qualquer rota. Para isso, você pode usar router.pathname em vez de /blog.

    router.push(`${router.pathname}?${newParams.toString()}`);
    
  2. Evitar Renderizações Desnecessárias: Cada vez que você chama router.push(), você está causando uma nova renderização da página. Se você estiver atualizando vários estados de URL ao mesmo tempo, isso pode levar a várias renderizações. Uma solução seria usar uma função debounce ou um callback para agrupar várias atualizações.

  3. Tratamento de Valores Complexos: O hook atual lida apenas com strings. Se você quiser armazenar valores mais complexos (como objetos ou arrays) na URL, precisará serializá-los e desserializá-los. Por exemplo, usando JSON.stringify() e JSON.parse().

  4. Integração com useState: Para tornar o hook mais familiar para os desenvolvedores React, você pode integrar a lógica do useState:

    const [internalValue, setInternalValue] = useState(value);
    

    E então sincronizar internalValue com a URL. Isso permite que você use o hook exatamente como usaria o useState, mas com a persistência na URL.

  5. Segurança: Tenha cuidado ao ler valores da URL e usá-los diretamente em seu aplicativo. Valores maliciosos podem ser inseridos na URL. Sempre valide e/ou sanitize os valores antes de usá-los.

  6. Histórico de Navegação: Cada vez que você atualiza a URL, uma nova entrada é adicionada ao histórico de navegação do navegador. Isso pode não ser o comportamento desejado, especialmente se o usuário estiver apenas ajustando um filtro. Você pode considerar usar router.replace() em vez de router.push() para evitar isso.

  7. Fallback para Navegadores sem suporte: Embora a maioria dos navegadores modernos suporte a API URLSearchParams, pode ser uma boa ideia incluir um polyfill ou verificar a disponibilidade antes de usar.

Em resumo, o conceito de armazenar o estado na URL é poderoso, mas é importante considerar as implicações e nuances de sua implementação. A abordagem apresentada é um bom ponto de partida, mas pode ser aprimorada e adaptada de acordo com as necessidades específicas do projeto.

2

Ótimos pontos! Criei esse código pensando no meu caso de uso e acabei esquecendo de generalizar ele. Vou atualizar o código com suas dicas!

1

Olha que legal a volta de como era feito antigamente!
SPA é muito ruim por isso, vc quer mandar algo pra alguém e não pode mandar exatamente
aquilo, via URL.

Que bom que isso esteja voltando

1

Sim! Sempre achei ruim quando favoritava o link de uma dashboard, mas quando entrava nela estava tudo desconfigurado. O interessante é que também da para salvar estados em SPAs, como com React Router.