Gerenciamento Fácil de Requisições no React usando bibliotecas @zup-next: Parte 1

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

Louco por javascript e apaixonado por computação

Nós aqui da Zup, geralmente usamos um conjunto de utilitários quando precisamos gerenciar requisições no React. Recentemente, decidimos compartilhar essas ferramentas através de dois repositórios: redux-resource e redux-action-cache. Neste artigo, falaremos sobre o primeiro, que é responsável por criar quase tudo que precisamos para controlar nossas requisições através das ações e estado do redux.

Sejamos sinceros, criar tipos de ações (constantes), action creators, reducers e sagas para todas as requisições é bem chato. Isso leva tempo e, às vezes, fica difícil de manter. Quando usamos serviços REST, todo GET, PUT, POST ou DELETE é basicamente a mesma coisa. Porque estamos recriando o código toda vez que precisamos chamar um novo serviço?

E se também tratarmos nossas requisições como recursos REST no front end? Não poderíamos então usar o mesmo tratamento para todas as requisições e evitar repetição de código? É exatamente essa a intenção da nossa biblioteca redux-resource.

Uma aplicação usando nossa biblioteca

Este artigo apresenta uma aplicação simples criada com o redux-resource. A aplicação se trata de uma loja online de filmes que permite o usuário verificar seu saldo atual, ver o catálogo de filmes e fazer compras. O foco aqui é em como gerenciar as requisições, portanto não entraremos em detalhes quanto aos estilos ou outros componentes react.

Leia também o artigo React Hooks: o que é como funciona?

O projeto demo com o código completo pode ser encontrado aqui. Você pode seguir as instruções no readme do projeto para executá-lo. As imagens a seguir ilustram o funcionamento da aplicação:

interface loja de filmes
Tela 1: home

Na Tela 1, o usuário confere o saldo e o catálogo de filmes disponíveis para compra.

tela loja de filmes
Tela 2: detalhes do filme

Tela 2: detalhes do filme

detalhes do filme
Tela 3: pagamento

A Tela 3 é responsável por efetuar o pagamento e finalizar a compra.

Na primeira parte desta série de três artigos, utilizaremos a Tela 1 e a Tela 2para mostrar o uso básico da biblioteca. Na segunda parte, a Tela 3 para que seja possível efetuar o pagamento de compras.

Na terceira e última parte, trataremos o problema onde múltiplas requisições são disparadas para carregar um mesmo recurso no decorrer do ciclo de vida da aplicação, introduzindo a biblioteca redux-action-cache.

Escrevendo a aplicação

A API REST que estamos utilizando para construir a aplicação consiste dos seguintes serviços:

  • GET /profile: recupera o perfil do usuário
  • PUT /profile: atualiza o perfil do usuário de acordo com os dados passados no corpo da requisição
  • GET /catalog: recupera a lista de filmes disponíveis
  • GET /wallet: recupera a carteira do usuário. A carteira consiste de um objeto com duas propriedades: balance e cards. “balance” é um número representando o saldo e “cards” é a lista de cartões de crédito.
  • POST /wallet: adiciona um novo cartão de crédito à carteira
  • DELETE /wallet: remove um cartão de crédito da carteira
  • POST /order: cria uma compra

Atenção: apesar das requisições http serem GET, POST, PUT ou DELETE, as operações definidas no redux-resource são load, create, update e remove.

Podemos identificar três recursos ao analizar as duas primeiras telas: o perfil do usuário, o saldo (carteira) e o catálogo de filmes. A primeira coisa que precisamos fazer é criar as funções da API. Recomendamos usar a biblioteca axios para isso.

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

Agora que definimos a interface da API, podemos criar os recursos:

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

Simples assim! Todo recurso pode ter as operações load, update, create ou remove. Neste exemplo, usamos as operações update profile, create wallet e remove wallet apenas para ilustrar como elas poderiam ser usadas. De fato, não faremos chamadas a nenhuma delas no decorrer do código.

A função createResource retorna um objeto com as chaves:

  • types: os tipos das ações (strings)
  • actions: os actions creators (funções que criam ações)
  • reducers: os reducers a serem providenciados ao redux
  • sagas: as sagas a serem providenciadas ao redux-saga

Utilizamos o valor de retorno de createResource para criar nossa store e disparar ações. O código seguinte mostra como criar o store do redux com os recursos que criamos.

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

Acima, combineReducers é uma função típica do redux, enquanto createEffects é uma função utilitária da nossa biblioteca responsável por criar os efeitos do redux-saga, ela recebe um objeto onde as chaves correspondem ao tipo das ações e os valores correspondem as funções generator.

Para mais detalhes em relação à função createEffects, por favor leia nossa documentação.

Agora que configuramos os recursos e o store do redux, podemos começar a usá-los! Nós dividimos a Tela 1 e a Tela 2 em três containers:

  • Header: responsável pelo cabeçalho, compartilhado por todas as telas
  • Home: responsável pelo conteúdo da Tela 1
  • Movie: responsável pelo conteúdo da Tela 2

Abaixo, apresentamos o código do container Header:

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

Dentro da parte componentDidMount do ciclo do react, disparamos a operação load para os recursos “profile” e “wallet”. Na função render, checamos o estado dos recursos e, baseado neles, decidimos o que mostrar na tela. Os nomes isPristine, isLoading e hasLoadError são funções utilitárias providenciadas pela nossa biblioteca, mas se elas não te agradam, você pode também verificar o estado da operação diretamente:

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

Existem quatro estados possíveis para uma operação de um recurso: “pristine”, “pending”, “success” e “error”. Primeiro verificamos “pristine”, pois uma renderização é feita antes das operações de load serem disparadas e não queremos renderizar nada nesse caso.

As respostas para nossas requisições são retornadas no campo “data” dos recursos. Quando definimos a API (primeiro código apresentado neste artigo), dissemos ao axios para retornar apenas o payload na resposta, portanto, no nosso caso, “data” contém exatamente o payload das respostas retornadas pelo servidor.

Podemos usar a mesma ideia para construir os demais componentes.

Abaixo, mostramos o código para o container Home:

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

😎E o container Movie:

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

No container Movie.js , não estamos interessados no catálogo completo, mas apenas no filme com id igual ao parâmetro passado na URL. Portanto, selecionamos o filme que queremos do catálogo usando findMovieById. Em aplicações reais, seria preferível utilizar o reselect para memoizar o filme, já que rodar um findBy dentro do método render pode ser muito caro.

separador

Legal! Já criamos três dos nossos quatro containers! Nesta parte conseguimos implementar a maior parte da aplicação usando três request diferentes sem escrever se quer uma linha de ação, reducer ou saga! Pode até não parecer muito nesta aplicação simples, mas isso economiza muito tempo em apps reais.

Além disso, também criamos uma forma muito bem definida de escrever nossas interações com as APIs, o que ajuda, e muito, na manutenção do código.

Parte 2

Na parte seguinte (Parte 2 - continuação desse artigo) vamos implementar a terceira tela: a interface de pagamento. Para isso, vamos usar a operação “create” e algumas outras ações para controlar seu comportamento. Nos vemos lá!

Interessados em hooks, typescript e cache? O projeto demo-typescript, em nosso repositório de demos, implementa esse mesmo projeto usando typescript ao invés de javascript. O projeto demo-hooks, além de usar typescript, usa hooks e ainda configura um cache inteligente para a aplicação (spoiler alert: hooks rocks!). Sobre o cache, ainda vamos falar bastante sobre isso na terceira e última parte desta série de artigos, aguarde.

Você pode ter notado também a natureza estática dos recursos que criamos. Se chamarmos o load de um recurso duas vezes, por exemplo, o conteúdo atual seria substituído pelo novo. Esse comportamento funciona muito bem para a maioria das situações, mas e se quiséssemos recuperar dois filmes (através de uma URL do tipo “/movies/{id}”) e exibí-los lado-a-lado?

A biblioteca redux-resource também disponibiliza a função createDynamicResource exatamente para esses casos. Se precisar usá-la, dê uma olhada na nossa documentação e no demo demo-dynamic-resource.

Deixo aqui meus agradecimentos aos demais autores da biblioteca e revisores deste texto: Isac e Raphael de Souza. ❤️

Você é um profissional da área e quer fazer a diferença junto com uma equipe fora da curva?

Veja aqui todas as nossas vagas em desenvolvimento.

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.