Gerenciamento de Requisições React com bibliotecas @zup-next: P3

7/8/2019
Tiago Peres França
Tiago Peres França
Front-end Developer

Louco por javascript e apaixonado por computação

Está sem tempo para ler? Aperte o play para escutar o artigo.

Chega mais na terceira e última parte da nossa série de artigos sobre Requisições no React. Se você ainda não leu os anteriores, veja aqui Parte 1 e Parte 2. O código completo para a aplicação demo que estamos construindo nesta série pode ser encontrado aqui.

Motivação

Nós que trabalhamos com front-end, estamos diretamente relacionados à boa experiência do usuário com nossas aplicações. Cada micro interação, ajustes e melhorias que fazemos, são direcionadas para melhor a experiência do usuário.

Na Zup, essa preocupação se tornou evidente quando percebemos em um dos nossos projetos que, apesar de termos uma aplicação funcional e um bom feedback do cliente, a experiência do usuário poderia ser melhorada com a redução no número de “loadings” através de um controle mais inteligente das requisições.

Foi nessa linha de pensamento que resolvemos criar a biblioteca que é base dessa última parte da série. A seguir, iremos explicar como o uso da biblioteca melhorou consideravelmente vários aspectos para nossos clientes e para nós desenvolvedores.

Melhorando a aplicação demo

Voltando na nossa aplicação demo, podemos dizer que ela já foi implementada e funciona muito bem, mas ainda podemos fazer uma melhoria. Se você abrir o painel Network, nas ferramentas de desenvolvedor do Chrome, verá que alguns recursos são chamados, na maioria das vezes, desnecessariamente.

A imagem abaixo mostra o estado do painel Network na tela de catálogo, após a compra de um filme.

demo
Painel Network após a compra de um filme e exibição do catálogo (sem cache)

Veja que a URL “/catalog” foi chamada três vezes e a URL “/wallet” foi chamada duas vezes.

Como as operações de load acontecem no componentDidMount de cada componente, sempre que esses componentes são mostrados na tela, as requisições são disparadas. Em várias aplicações nos deparamos com situações desse tipo, em que uma página precisa de um recurso que já foi carregado por outra ou então o componente está sendo mostrado pela segunda vez e não precisa refazer a busca.

Poderíamos carregar os recursos na primeira página do fluxo e não carregar no restante, mas essa é uma péssima ideia. E se o usuário entrar diretamente pela URL? Além disso, ao inicializar cada container, teríamos que saber, a priori, se os recursos já foram carregados ou não, o que complicaria nosso código.

Outra solução seria verificar todas as vezes se o recurso já foi carregado antes de carregá-lo novamente. Mas em algumas situações, é realmente necessário recarregar um conteúdo, portanto, acabaríamos criando uma série de “ifs” que poderiam se tornar bem complexos no futuro.

Leia também Java vs Kotlin: Vantagens, Desvantagens e Performance

A solução que queremos é uma que seja capaz de lembrar os recursos que já foram carregados e esquecê-los nas horas certas, mas sem ter que adicionar complexidade ao código dos nossos containers. Nos containers, queremos continuar com a simplicidade de disparar a ação load de um recurso sempre que precisamos utilizá-lo, sem se preocupar se ele já foi carregado ou não.

Ou seja, precisamos de um sistema inteligente de cache que pode ser configurado de fora dos containers. Para esse sistema demos o nome de redux-action-cache.

Como todas as nossas ações de fetch são gerenciadas pelo redux-saga, surgiu a ideia de interceptar as ações do redux e tomar decisões de acordo com a necessidade.

redux-action-cache

O redux-action-cache é um middleware que permite cachear ações do redux, ou seja, quando uma ação é disparada, antes que ela possa prosseguir, ela passa por nosso middleware que age como um gateway, dizendo se a ação será ignorada ou se ela pode prosseguir para os demais middlewares e reducers.

Uma ação é ignorada sempre que existe um cache válido para ela. A aplicação começa com um cache vazio, assim que uma ação é disparada, caso exista uma regra de cache para ela, um cache é criado, de forma que, caso ela seja disparada novamente, será ignorada até que esse cache não seja mais válido.

Como configurar

Para configurar o redux-action-cache é bem simples, basta definirmos as regras para nossa aplicação e adicionar o middleware ao redux. Atenção: nosso middleware precisa ser o primeiro a ser definido para que ele funcione como um gateway! Não adianta ignorarmos uma ação depois que ela já tenha passado pelo redux-saga, por exemplo.

Voltando para a nossa aplicação de loja de filmes digitais, podemos extrair as seguintes regras de cache:

  • Cachear todas as ações onde “type” termina com “/LOAD”. Esses tipos de ações são definidos pelo redux-resource. São o valor do campo “types” no resultado da função “createResource”. Veja aqui uma lista com todos os tipos.
  • As ações de load não devem ser cacheadas se acontecer algum erro durante a requisição. Ou seja, se dispararmos um “/LOAD” após um “/LOAD_ERROR”, “/LOAD” deve ser processado pelo redux, independente de ter sido cacheado anteriormente. Em outras palavras, todo “/LOAD_ERROR” deve invalidar o cache de seu “/LOAD” correspondente.
  • A ação load do recurso “wallet” deve ser, obrigatoriamente, recarregada após uma compra ser efetuada com sucesso. Ou seja, uma ação com tipo “ORDER/CREATE_SUCCESS” deve invalidar o cache da ação “WALLET/LOAD”.

Tendo definido as regras de cache da nossa aplicação, basta  escrevê-las no formato esperado pela biblioteca redux-action-cache.

<p> CODE: https://gist.github.com/Tiagoperes/034bbca8938d7f347d8debb5c455cfb6.js</p>

No código acima, em “include”, definimos um array com todos os tipos de ações que devem ser cacheadas. Outra maneira de atingir o mesmo resultado seria utilizando uma regex. Veja o exemplo abaixo:

<p> CODE: https://gist.github.com/Tiagoperes/9872df1985047afb145fbdd244828ff8.js</p>

Nas invalidações, precisamos utilizar regex e grupos de captura para definir que toda ação que segue o formato “(x)/LOAD_ERROR” deve invalidar a ação “(x)/LOAD”. Ao invés de usar regex, poderíamos ter definido todas as ações de erro e todas as ações de load, mas o código ficaria bem mais longo.

Ao criar o store do redux, basta incluirmos nosso middleware:

<p> CODE: https://gist.github.com/Tiagoperes/e433616be30cf35bb55b1b497d2bdb13.js</p>

Pronto! Simples assim.

Agora se executarmos a aplicação novamente, comprando um filme e retornando ao catálogo, veremos que são feitas apenas as requisições necessárias para o funcionamento da aplicação.

requisições
Painel Network após a compra de um filme e exibição do catálogo (com cache)

Agora, o catálogo é chamado apenas uma vez! Apesar de vários containers utilizá-lo, o catálogo é um só, não precisamos carregá-lo o tempo todo. A “wallet” continua sendo chamada duas vezes, pois definimos que seu cache deve ser invalidado sempre que uma compra é feita.

A economia é pouca nesta aplicação que é simples, mas em situações reais, o redux-action-cache faz uma diferença enorme e isso com pouquíssimas linhas de código!

No começo desse nosso papo, falamos sobre a importância de desenvolvedores frontend se importarem de forma minimalista com a experiência que proporcionam ao usuário. Essa abordagem do redux-action-cache nos permitiu melhorar a comunicação do usuário com a aplicação, diminuindo a quantidade de requisições feitas ao servidor, o que trouxe diversos benefícios, como a redução no número de loadings da aplicação e redução na carga do servidor.

Aqui utilizamos uma configuração simples para o cache, mas a biblioteca oferece várias opções de customização como: validade máxima, persistência e cacheamento baseado em outras propriedades que não sejam o tipo da ação.

Para mais informações, consulte aqui nossa documentação.

Pronto! Chegamos ao fim. No decorrer desses três artigos criamos uma aplicação com as bibliotecas redux-resource e redux-action-cache. Através dessas bibliotecas, foi possível simplificar o código no que diz respeito ao gerenciamento de requisições. Além disso, criamos um cache inteligente de fácil configuração. Obrigado pela leitura e se tiver dúvidas ou sugestões, não hesite em utilizar o Github para criar issues ou submeter pull requests.

Abaixo, deixo uma lista com os links mais úteis relacionados ao conteúdo desta série.

  • Redux-resource
  • Redux-action-cache
  • Redux
  • Redux-Saga
  • Projeto de demonstração no Github
  • Projeto de demonstração no Github (typescript)
  • Projeto de demonstração no Github (typescript e hooks)

  • Aproveito para deixar um agradecimento aos demais autores da biblioteca e deste artigo: Isac e Raphael de Souza

    O que você achou deste conteúdo?
    Quer receber nossos conteúdos?
    Seu cadastro foi efetuado com sucesso! Enviaremos as novidades no seu email.
    Oops! Something went wrong while submitting the form.