Sua aplicação é um labirinto? Usabilidade e URL's
Tags: braziliandevs, ui, ux, frontend
Published: 2024-02-15 11:06:33

De tempos em tempos, aparecem pessoas comentando sobre a possibilidade do uso de *query strings* ([referência](https://en.wikipedia.org/wiki/Query_string)) para armazenar estados ao invés de usar estados "invisíveis" e não rastreáveis (mais adiante você entenderá o que eu quero dizer com não rastreáveis) como se fosse algo super inovador. 

A verdade é que estamos voltando às origens.

- [Introdução](#intro)
- [Estados Invisíveis](#estados-invisiveis)
 - [O problema](#problema)
 - [As soluções](#solucoes)
- [Infinite Loading](#infinite)
- [Navegação em Abas](#abas)
 - [Contraponto: O Contexto que importa](#abas-contexto)
- [Filtros de busca](#filtros)
 - [Atualizando a listagem filtro por filtro](#filtros1)
 - [Selecionando todos os filtros e aplicando](#filtros2)
- [Modais e Caixas de Diálogos](#modais)
- [Dicas de como melhorar esses pontos da usabilidade](#dicas)
 - [Estude sobre UX](#dicas1)
 - [Benchmarking](#dicas2)
 - [Pense como um usuário](#dicas3)
- [Referências](#ref)
- [Produtos Mostrados no Texto](#produtos)

## Introdução <a name="intro"></a>

Esse texto tem como objetivo mostrar, de forma conceitual, casos de uso em que esse padrão é bastante utilizado e que, ao longo do tempo, começou a ser negligenciado, principalmente em SPA's (Single Page Applications).

Não será objetivo, aqui, explicar como devem ser feitas as implementações desses comportamentos em frameworks ou bibliotecas. Ainda assim, quando possível, citações e links de referências para documentações serão adicionados.

> Partes do texto serão ilustradas com imagens e GIF's de aplicações, reais, em produção. O objetivo não é críticar o trabalho dos seus desenvolvedores.
>
> É importante dizer, também, que algumas dessas animações, em GIF's, podem demorar a carregar, porém esse é um "manual" ilustrado que não seria a mesma coisa sem elas.

## Estados Invisíveis <a name="estados-invisiveis"></a>

O primeiro tema que iremos discutir são os estados invisíveis e não rastreáveis (esse nome é, de fato, ruim e não sei o motivo pelo qual decidi usar, mas quando falo de rastreabilidade é, basicamente, a rastreabilidade quanto ao histórico do navegador e nos próximos tópicos você entenderá isso).

### O problema <a name="problema"></a>

Durante o processo de criação de aplicações front-end é comum que sejam utilizados estados ([useState](https://react.dev/reference/react/useState) no **React** ou [variáveis ou propriedades das classes](https://angular.io/guide/architecture-components) no **Angular**) para gerenciar o estado de componentes que lidam com fluxo de dados, como por exemplo **filtros** e **paginação**.

Isso resolve o problema: lida com o gerenciamento de estado do fluxo de dados desses componentes. Porém, isso também introduz um novo problema: esses estados são invisíveis e não registram histórico no navegador.

> Por exemplo, imagine que tenhamos uma página de estoque, com uma lista de produtos, paginados. Ao entrar nessa página, clicamos para abrir a página 2 e, então, clicamos em voltar, no navegador, voltamos pra página inicial. Veja o GIF abaixo:

![Problema ao clicar em voltar numa página](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqc2l37ei8yih65e103b.gif)

Isso é uma falha na usabilidade do produto, que atinge, inclusive, páginas famosas. Vejam, por exemplo, a página **Todos os Cursos** do [Eu Capacito](https://www.eucapacito.com.br/todos-os-cursos/):

![Todos os Cursos do Eu Capacito](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jj5yff9jk8ppnv4x8ock.gif)

Percebam que um fator em comum nesses exemplos é que ao clicarmos em algum número de página a URL não é alterada.

### As soluções <a name="solucoes"></a>

Existem, pelo menos, duas soluções ideais para esse problema. Ambas passam pelo uso da URL como estado da página.

#### Query String <a name="query"></a>

A primeira solução é utilizar a *query string* para determinar estados. Por exemplo: podemos acrescentar `?page=2` ou `?p=2` na URL para indicar a página e a aplicação consegue lidar com esse parâmetro para determinar qual a página que deve ser exibida.

Um exemplo de produto bastante conhecido, que utiliza *query strings* para paginar os dados é o **GitHub Issues**:

![Paginação por query parameter](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/109zvamlvgjuglgww2pn.gif)

Essa solução pode ser implementada em diversos **frameworks**, como, por exemplo, em [Angular](https://angular.io/guide/router#accessing-query-parameters-and-fragments) e em **React** com [react-router-dom](https://reactrouter.com/en/main/hooks/use-search-params).

#### Pathname <a name="path"></a>

Outra forma de solucionar esse problema é utilizar o [pathname](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname) para armazenar esses estados. Por exemplo, imagine as seguintes rotas: `categoria/marketing/` e `categoria/marketing/page/2/`. É claramente perceptível que a primeira rota indica a página 1 e que a segunda rota indica a página 2.

Esse exemplo foi retirado do blog [RockContent](https://rockcontent.com/br/blog/categoria/marketing/page/2/).

## Infinite Loading <a name="infinite"></a>

Existe um outro método de paginação de dados que é o *infinite loading* que carrega e agrega novos itens a parte de baixo a medida que é necessário (seja pelo scroll, seja por um botão de carregar mais). Esse tipo de solução é bastante utilizado quando o conteúdo não tem o próposito de ser bem estruturado (ter começo, meio e fim, por assim dizer). Complicado? vamos explicar com mais calma:

Imagine que você entra na home-page de um site de notícias: você faz isso pra descrobrir as notícias mais recentes e não quer saber quantas páginas de notícia existem.

Você quer rolar a tela até chegar numa data, ou numa notícia que já viu, e ai saberá que o que vem abaixo não lhe interessa mais.

Agora, por outro lado, para que você entenda que certo e errado depende do contexto, imagine que o usuário, vendedor em um marketplace, queira analisar todas as vendas que fez, então ele começa a rolar a página, mais itens são carregados, continua rolando, mais itens são carregados, mas perceba que ele não sabe nem quantos itens ainda faltam pra serem carregados e, o pior ainda, se ele apertar no botão de voltar, como em praticamente todo sistema que usa essa estratégia, ele voltará para a página anterior, e não para a última parcela das compras que foi carregada, pois não há marcação de paginação na URL.

> Um exemplo disso pode ser visto no feed do **Twitter** ou do **LinkedIn**, percebam que a ideia do feed é a descoberta de novos itens, logo, sem estrutura de começo, meio e fim.

## Navegação em Abas <a name="abas"></a>

Outro lugar onde esse é um problema de usabilidade claro são no uso das abas, veja o seguinte comportamento:

![Orion Business](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6h06pdpl1kl4bvflq63x.gif)

Perceba que, após alterar a aba, que é o conteúdo principal da página, e clicar em voltar, no navegador, voltamos a página inicial e não a aba anterior. Em alguns casos, mesmo que haja o mapeamento para as URL's, o comportamento não é padrão no browser, veja o comportamento na seguinte página (observe a URL e o comportamento do clique no botão de voltar):

![Woovi](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g2qj369udbw8g0uut37x.gif)

Veja um caso de uso onde o mapeamento em URL e o comportamento do navegador funcionam de forma satisfatória:

![Shopee](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/priv9c2wz82wiz81e6ss.gif)

### Contraponto: O Contexto que importa <a name="abas-contexto"></a>

O contexto é o que mais importa. Manter a aba ativa mapeada na URL é uma tática muito legal quando o conteúdo principal é o componente de abas. Agora, imagine que o componente de abas aparece no meio de uma página, entre diversos outros conteúdos: guardar essa aba ativa na URL não faz a menor diferença. Veja o caso da loja da [Steam](https://store.steampowered.com/):

![Exemplo de Usabilidade da Steam](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gznmtn69wd20yi9v5kg2.gif)

Nesse caso, vale a mesma lógica que comentamos sobre o uso de abas para descoberta de conteúdo no *Infinite Loading*.

## Filtros de busca <a name="filtros"></a>

Comentamos muito sobre paginação, navegação em abas, mas o mesmo se aplica para filtros. Como exemplo, podemos olhar a página de produtos, em quase todos os e-commerce com as filtragens: geralmente todas são mapeadas na *query string* da URL.

Ao mapear os filtros é importante levar em consideração o comportamento dos filtros com relação a filtragem:

1. sempre quando o usuário alterar um filtro, a listagem já se atualiza com os novos dados;
2. o usuário altera todos os campos e depois clica em aplicar os filtros.

Algumas das características desses dois comportamentos serão discutidos agora.

### Atualizando a listagem filtro por filtro <a name="filtros1"></a>

Observe que a filtragem de um e-commerce, como por exemplo o site da Hering, geralmente se aparenta com categorias:

![E-commerce da Hering](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i833uv6sbij5iy2k4ake.png)

Nesse caso, geralmente, usam-se o comportamento de a cada item selecionado a listagem já é atualizada. Percebam, também, que, por conta da categorização dos filtros, o histórico é preservado:

> Clicamos em Feminino -> Alfaiataria -> Alongado. Após esse fluxo, clicamos em voltar no navegador e voltamos para somente Alfaiataria. Clicamos novamente em voltar e ficamos somente na página de Feminino.

É visível que, a depender da quantidade de filtros e da forma como se pretenda usar esses filtros, essa pode não ser a melhor escolha.

### Selecionando todos os filtros e aplicando <a name="filtros2"></a>

Esse comportamento é, geralmente, mais utilizado quando os filtros possuem uma aparência de filtragem e não de categoria. 

Por exemplo, quando os filtros incluem datas de início e de fim, tem-se o seguinte caso de uso: altera-se a data de início pra depois da data de fim. A nova data de início e a data de fim que já estava preenchida não faz sentido.

Um exemplo disso é a tela de extrato da NuInvest:

![Extrato da NuInvest](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tbzv3wkgi04xmjbwgu1b.png)

Outro exemplo de uso, bastante comum, para esse tipo de comportamento é quando a API que devolve a listagem é pesada, mal desenhada, não otimizada, etc. e demora muito pra carregar os dados.

## Modais e Caixas de Diálogos <a name="modais"></a>

Modais e caixas de diálogos são muito bons em diversas situações, mas as vezes esquecemos um detalhe muito importante sobre eles: eles são, basicamente, uma nova página dentro da nossa própria página.

Desse modo, perceba o seguinte comportamento ao tentar escrever uma publicação no LinkedIn e resolver voltar:

![Comportamento do LinkedIn](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mb8kgkdz7wt1mibjkgyd.gif)

Um dos passos percorridos pelo usuário para chegar ao modal foi perdido ao clicar em voltar.

Para evitar esse tipo de problema, o estado dos modais também devem ser mapeados na URL, de alguma forma. Existem diversas formas de se implementar isso e um exemplo, não convencional, é o Trello que usa o `id` do card na barra de endereços:

![Modal no Trello](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/emcrcc2m4bcvq5ddb6v0.png)

## Dicas de como melhorar esses pontos da usabilidade <a name="dicas"></a>

Existem algumas formas de melhorar a usabilidade de aplicações quanto ao mapeamento de URL's. Nas seções abaixo iremos falar sobre algumas dicas de como melhorar isso.

### Estude sobre UX <a name="dicas1"></a>

Ler sobre UX é uma boa forma de aprender o comportamento de determinadas interfaces. Isso é interessante mas não resolve, de todo, o problema. Vide o texto [UX: Modais, quando (e quando não) usá-los](https://medium.com/@allanroubertie/ux-modais-quando-e-quando-n%C3%A3o-us%C3%A1-los-8079d8200a26), texto oriundo da **Nielsen Norman Group**, que não cita o uso das URL's.

### Benchmarking <a name="dicas2"></a>

Faça benchmarking em produtos que usa no seu dia a dia. Entre, investigue o comportamento, clique em voltar, clique em avançar, feche o modal clicando no ícone de fechar e clique em voltar. Selecione os filtros, clique em voltar, avançar, mude os filtros um a um, tente mudar a data pra datas inválidas (a data de início posterior a data de fim, por exemplo).

Entenda como os componentes reagem a todos esses casos.

### Pense como um usuário <a name="dicas3"></a>

Além de fazer benchmarking, pense como o seu usuário final. O que você achou do comportamento que você encontrou nos teus benchmarkings?

## Referências <a name="ref"></a>

- [Query String](https://en.wikipedia.org/wiki/Query_string)
- [useState](https://react.dev/reference/react/useState)
- [Introduction to components and templates](https://angular.io/guide/architecture-components)
- [Common Routing Tasks](https://angular.io/guide/router#accessing-query-parameters-and-fragments)
- [useSearchParams](https://reactrouter.com/en/main/hooks/use-search-params)
- [pathname](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname)
- [UX: Modais, quando (e quando não) usá-los](https://medium.com/@allanroubertie/ux-modais-quando-e-quando-n%C3%A3o-us%C3%A1-los-8079d8200a26)

### Produtos Mostrados no Texto <a name="produtos"></a>

- [Catálogo StayApp](https://catalogo.stayapp.com.br/)
- [Eu Capacito](https://www.eucapacito.com.br/todos-os-cursos/)
- [RockContent](https://rockcontent.com)
- [GitHub](https://github.com/)
- [Orion Complex](https://orioncomplex.com.br/)
- [Woovi](https://woovi.com/)
- [Shopee](https://shopee.com.br/)
- [Steam](https://store.steampowered.com/)
- [Hering](https://hering.com.br/)
- [NuInvest](https://nuinvest.com.br/)
- [LinkedIn](https://www.linkedin.com/)
- [Trello](https://trello.com/)

---

Foto de <a href="https://unsplash.com/pt-br/@aronvisuals?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Aron Visuals</a> na <a href="https://unsplash.com/pt-br/fotografias/pessoa-segurando-bussola-preta-e-verde-apontando-para-oeste-3jBU9TbKW7o?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>