Aprenda a usar Redux nos seus APPs React na prática

O Redux é uma ferramenta fundamental em grande parte dos APPs construídos com React, por tanto dominar o uso dele é extremamente importante 😁

Neste artigo você vai aprender os fundamentos do Redux na prática criando uma página de loja de livros do zero.

Bora começar essa jornada?

 

O que é Redux?

Redux é uma biblioteca JavaScript criada por Dan Abramov e Andrew Clark (inspirada na arquitetura Flux do Facebook) que tem como objetivo gerenciar estados globais da nossa aplicação front-end, seguindo a arquitetura Flux (vamos ver adiante).

Dessa forma o Redux ficará responsável por:

  • Retornar os estados globais solicitados pelos components;
  • Abstrair a lógica de negócio dos estados, disponibilizando uma interface simples para os componentes requisitarem algumas ações sobre os estados, sem se preocuparem de como isso será feito;
  • Manter os componentes atualizados, sempre que os estados forem alterados.

Ou seja, ele resolve os problemas de compartilhamento de estados globais e cria uma interface simples para os componentes utilizarem.
Podemos ter como exemplo o carrinho de compras e perfil de usuário, que são dados utilizados por vários components.

 

O que vamos criar?

Para entender como o Redux nos auxilia, vamos criar uma aplicação web de uma loja virtual de livros, chamada de OneBitBooks.
Neste app, utilizaremos o Redux para gerenciar o estado do carrinho de compras, permitindo que várias páginas e components tenham acesso de forma simples aos dados deste estado.

 

Página de listagem de livros:

 

Página de detalhes:

Para acessar os códigos completos do projeto: https://github.com/OneBitCodeBlog/onebitbooks

 

O que vamos usar:

  • Node.js
  • React.js
  • Axios
  • Json Server
  • Redux
  • React Router Dom
  • React Icons

 

 

Passo a Passo da criação do APP

Criando projeto

1 – Para criar nosso projeto, execute o comando:

 

Limpando projeto

Após isso, vamos limpar o nosso projeto.

1- Primeiro remova os seguintes arquivos que não vamos utilizar:

  • public/favicon.ico
  • public/logo192.png
  • public/logo512.png
  • public/manifest.json
  • public/robots.txt
  • src/App.test.js
  • src/App.css
  • src/logo.svg
  • src/serviceWorker.js
  • src/setupTests.js

 

Em seguida, vamos limpar o conteúdo de alguns arquivos:

2 – No arquivo public/index.html, deixe-o da seguinte forma:

3 – No src/App.js vamos deixá-lo dessa forma:

4 – No src/index.css vamos deixar em branco para colocar nossa própria estilização global mais para frente.

5 – No src/index.js deixe-o da seguinte forma:

 

Configurando rotas

Neste projeto teremos duas páginas, a Home que é mostrado a lista de produtos e Cart que é a página do carrinho com todos os itens.

1 – Para isso crie a seguinte estrutura:

Para que não tenhamos problemas na configuração da rota, precisamos ter um componente básico criado em cada página.

2 – No src/pages/Cart/index.js poderá deixar da seguinte forma:

3 – No src/pages/Home/index.js poderá deixar da seguinte forma:

4 – Instale o React Router Dom, que será responsável pelo roteamento das páginas:

5 – Crie o arquivo src/routes.js que terá as configurações das nossas rotas, ele ficará da seguinte forma:

6 – Em seguida no src/App.js, ao invés de renderizar a página diretamente, vamos chamar o nosso arquivo de rotas, que ficará responsável por decidir qual página será renderizada.

Após isso já vamos conseguir alternar entre as páginas.

 

Configurando o layout

Global

Vamos começar fazendo uma estilização que servirá para todas as páginas, resetando alguns valores padrões dos navegadores, definindo a cor do fundo, fonte, etc.

1 – Para isso modifique o arquivo src/index.css da seguinte forma:

 

Header

Como o Header é algo que será o mesmo nas duas páginas, será mais interessante criá-lo como um componet.
Antes de criar diretamente o component, vamos adicionar ao projeto seus recursos externos.

1 – Para baixar o logo, acesse https://www.flaticon.com/free-icon/book_710330?term=book&page=1&position=11 e escolha este ícone na cor #FFFFFF e tamanho 32px.

2 – Em seguida crie a pasta src/assets e coloque dentro o ícone que acabamos de baixar.

3 – Em seguida instale o pacote de ícones do React:

Agora sim podemos criar nosso component.

4 – Crie a pasta src/components/Header e dentro crie os arquivos index.js e styles.css.
Vamos começar com a estrutura da tela e já utilizar as nossas rotas.

5 – No src/components/Header/index.js o código ficará desta forma:

Agora vamos para a estilização do component.

6 – No src/components/Header/styles.css o código ficará desta forma:

7 – Com o nosso Header criado, vamos disponibilizá-lo em todas as telas, para isto basta inserí-lo no src/App.js.

 

Página Home

Esta é a página onde vamos listar todos os livros a venda.
Vamos começar pela estrutura da página, com alguns dados fixos por enquanto.

1 – No arquivo src/pages/Home/index.js deixe-o da seguinte forma:

Agora vamos partir para a estilização desta página.

2 – No arquivo src/pages/Home/styles.css deixe-o da seguinte forma:

 

Página Cart

Esta é a página onde exibirá os produtos selecionados e os totais da compra.
Vamos começar pela estrutura da página, com alguns dado fixos por enquanto.

1 – No arquivo src/pages/Cart/index.js deixe-o da seguinte forma:

Agora vamos partir para a estilização desta página.

2 – No arquivo src/pages/Cart/styles.css deixe-o da seguinte forma:


Integração com API

Ao invés de ter dados na nossa tela de forma estática na página Home, vamos consumir  estes dados através de uma API.

Para não precisarmos ter trabalho na criação de um backend para isso, vamos utilizar o json-server, que oferece dados via API através de um arquivo json como se fosse um banco de dados.

1 – Para instalar o json-server, vamos instalar o mesmo como dependência de desenvolvimento.

2 – E agora vamos criar na raiz do projeto o arquivo server.json que conterá os dados dos nossos livros.

3 – Para levantar o serviço do json-server na porta 3333, execute o comando:

Agora vamos configurar a nossa aplicação para pegar os dados da API, para isso vamos utilizar o Axios.

4 – Para instalá-lo execute o comando.

5 – Crie o arquivo de configuração do axios src/services/api.js.

6 – Feito isso, agora vamos novamente ao src/pages/Home/index.js para receber os dados via API dos produtos.

Desta forma, já teremos nossos dados vindo da API.

 

Configurando o Redux

Agora sim que temos toda a base do projeto criada, vamos finalmente ver o Redux.

Primeiramente vamos instalar o redux que trata do próprio Redux em si.
O react-redux que lida com a integração do Redux com o React.js, pois o Redux em si, pode ser utilizado também no angular, vue, dentre outros frameworks e bibliotecas JavaScript.

O @reduxjs/toolkit é uma biblioteca oficial do time do Redux e indicada por eles, que torna a configuração do Redux, muito mais fácil.

Feita a instalação, vamos entender rapidamente como é o fluxo de dados do Redux.

Na imagem abaixo, em roxo estão os elementos principais do Redux e em verde um componente genérico, que nosso caso seria o Header, Cart ou Home.

Os estados globais são armazenados na store, que é imutável.

Um component pode a qualquer momento solicitar diretamente os dados para a store e também será notificado quando os dados são alterados.
Se um component quer modificar algo um dado na store, ele deve chamar uma Action.

As actions são as ações possíveis que o component pode solicitar, como adicionar um item, remover um item, etc.
Vai ter action para cada ação e podem receber um determinado valor como parâmetro.
Quando uma action é chamada, o Reducer é acionado, e é ele que será responsável por atualizar os dados na store.
É ele que contém toda a lógica de negócio.

Essa que é a famosa arquitetura Flux, que o Redux implementa.

Vamos a seguir configurar toda essa estrutura do Redux e nos aprofundar mais sobre cada um desses elementos.

 

Store

A store é um único objeto que armazena todo o nosso conjunto de estados globais da nossa aplicação.
Este um único objeto segue o conceito de “um único ponto de verdade”, ou seja, ele garante a consistência e confiabilidade dos dados.
Além disso, esses dados são imutáveis, para reaproveitar as vantagens da programação funcional, evitando alguns problemas de estados inconsistentes.
Será através da store, que os components vão buscar os dados e ser notificados quando houver alguma alteração.

Dito isso, vamos criar nossa store.

1 – Crie a pasta src/store, que será onde ficará toda a nossa lógica do Redux.
Dentro desta pasta crie o arquivo index.js, onde vamos criar a nossa store.

2 – Agora, para que a nossa store fique disponível para nossa aplicação, vamos voltar no src/App.js e vamos utilizar o Provider do redux, que vai servir para a nossa aplicação a store que acabamos de criar.

Neste momento a nossa aplicação vai dar erro, mas calma que ainda temos que configurar mais algumas coisas.

 

Action

A action é um objeto que disponibiliza uma interface com as possíveis as ações, que os components podem utilizar.

Uma action basicamente tem 2 atributos essenciais:

  • type: É um código identificador, no formato de string, que servirá para o reducer identificar as actions, é como a carteira de identidade de cada action;
  • payload: São os dados que o component passa para a action, para realizar alguma ação.

Nem toda action tem um payload, mas em casos como uma action para adicionar itens a uma lista, ele precisa que o component passe esse item a ser adicionado.

Agora vamos criar nossa estrutura para configurar nossa action.

Para deixar nossa aplicação organizada e escalável futuramente, vamos criar uma pasta chamada src/store/modules, onde vamos separar os reducers e actions por responsabilidade.

1 – Crie uma pasta src/store/modules/cart que terá apenas a responsabilidade de gerenciar os dados relacionados ao carrinho.

Caso futuramente quiséssemos adicionar também estados para gerenciar perfil de usuário, autenticação, dentre outros, basta criar uma pasta para cada um dentro de src/store/modules/.

2 – Agora vamos criar o arquivo de nossas actions, o src/store/modules/cart/actions.js e dentro dele criar 3 actions para que nossos components possam utilizar.

a – A primeira para adicionar um item ao carrinho, que vamos chamar de addToCart.
b – A segunda para remover um item do carrinho, chamada de removeFromCart.
c – E uma terceira para atualizar a quantidade de cada item que está no carrinho, com o nome de updateAmaount.

Para isso, utilizaremos a função createAction, passando apenas o type (identificador).

Esse type é apenas uma string, mas o recomendado é seguir o padrão de colocar o prefixo com o nome da pasta o qual a action está imediatamente contida e depois da barra deve descrever o que a action faz.

Seu arquivo deve ficar assim:

Mas e o payload?

Graças ao Redux Toolkit, o payload é definido por baixo dos panos como um objeto flexível.
Além disso devemos exportar cada action para serem utilizadas pelo reducer e pelos components.

 

Reducer

Como foi citado antes, na nossa store os dados são imutáveis, mas em alguns momentos será necessário alterar esses estados, para isso existem os reducers, que são funções puras, responsáveis por “alterar” os estados.

Na verdade ele cria um novo estado com novos estados, assim como é feito nos estados de qualquer component.
O reducer também tem o papel de centralizar a lógica de alteração dos estados e evitar que cada component tenha essa responsabilidade.

 

1 – Para definir nosso reducer, crie o arquivo src/store/components/cart/reducer.js.

Nele importamos nossas actions criadas anteriormente.

A função createReducer, permite criar nosso reducer em si, ele recebe 2 parâmetros.
O primeiro é o estado inicial, ou seja, assim que nossa aplicação for iniciada, ele vai popular o state com esses dados.
O segundo é um objeto que conterá a nossa lógica de negócio para cada uma das actions.
Deverá passar a action como chave e como valor, uma função que recebe state e action.

O state vem com os dados da nossa store e o action contem os dados da nossa action com os atributos type e o payload.

É através do payload que vamos pegar os dados passados pelo component.

Considerando que o component passou como payload estes dados abaixo para a action addToCart:

Vamos analisar o código desta action para entender melhor:

Nele extraímos o payload da action e extraímos o id do item de carrinho que deverá ser adicionado.

Em seguida vamos tentar buscar na store se já existe esse item.
Caso ele exista, vamos apenar incrementa a quantidade dele no carrinho.
Caso ele ainda não exista, vamos adicionar o campo amount com quantidade 1 e adiciona-lo à store.

Note que, apesar de o state representar a store, que é imutável, podemos modifica-la como um objeto ou array qualquer e após isso será automaticamente alterado o estado global.

Isso ocorre, pois o Redux Toolkit faz a mágica por baixo do panos, facilitando nossa vida em modificar um estado imutável, sem precisarmos recorrer a técnicas de desestruturação, que podem se tornar um verdadeiro desafio dependendo do caso.

Tendo entendido esses conceitos, as demais ações ficam muito simples de se entender o que fazem.
Mas ainda falta um detalhe, precisamos vincular nosso reducer a store.

 

2 – Porém temos outro detalhe, para permitir no futuro que nossa store possa registrar mais de um reducer, precisamos de uma configuração a mais, para isso vamos criar primeiro um arquivo src/store/modules/rootReducer.js que vai centralizar todos os nossos reducers da aplicação.

Bastando utilizar o método combineReducers para adicionar todos os nossos reducers que vamos utilizar.

Agora sim, vamos pegar todos os reducers de uma só vez do rootReducer.js e adicionar na nossa store.

 

3 – Indo novamente em src/srote/index.js e vamos passar o rootReduce para o método configureStore.

Feito isso, nossa estrutura de configuração do Redux está pronta.

 

Redux em ação

Agora sim vamos ver o Redux em ação.

 

Página Home

1 – Vamos fazer nossa última alteração na página Home e ver a explicação em seguida.

Vamos entender o código por partes.

Primeiro importamos do react-redux dois módulos:

  • useDispatch: Faz a chamada de uma action.
  • useSelector: Recupera os dados da store.

Ele recebe uma função anônima como parâmetro, e essa função anônima recebe o  state como argumento, que representa o estado global.

Em seguida importamos nossas actions.

Depois é criado a constante amount que vai utilizar o useSelector para obter os dados da store, especificamente do cart, ou seja, do carrinho e vai armazenar a quantidade de cada item.

Em seguida vamos utilizá-lo para substituir o valor 0 estático da quantidade de cada livro no carrinho, por um valor dinâmico baseado no id do livro.

Agora será necessário criar uma instância do dispatch, para poder utiliza-lo.

Com o dispatch criado, que é quem dispara as actions, criamos a função handleAddProduct que recebe todos os dados do livro e chama a action addToCart passando os dados do livro como payload.

Agora no botão adicionar, adicionamos essa função passando o livro atual que está.

Assim, quando o usuário clicar, ele vai adicionar um livro ao nosso carrinho e automaticamente o amount será recarregado mostrando a quantidade atual do livro.

No próximo clique, no mesmo produto, ele vai apenas incrementar a quantidade.


Component Header

1 – O nosso header será mais simples, pois exibe apenas do lado direito da tela a quantidade dos livros no carrinho (components/Header/index.js).

Como precisamos apenas exibir os dados, será necessário apenas importar o useSelector.

Em seguida utilizamos ele para criar a constante cartSize que vai calcular a quantidade total de itens dentro do state.cart.

Agora é só exibir o cartSize de forma dinâmica.

Agora sempre que um livro for adicionado ou removido, essa quantidade no Header será alterada.


Página Cart

1 – Segue a linha de pensamento da página Home (components/pages/Cart/index.js).

Será utilizado o useDispatch e useSelector, então eles devem ser importados e o useDispatch instanciado.

A constante cart, vai coletar todos os nossos itens armazenados na store de cart, e vamos utilizar o método map para calcular o subtotal de cada item.

O cart é utilizado mais adiante para listar cada um dos itens do carrinho e suas respectivas informações.

Em seguida criamos a constante total, que calculará o valor total de todos os itens do carrinho.

Esse total é exibido no fim da página.

Agora com relação as actions, será declarado uma função increment para adicionar mais um a quantidade do item e decrement para subtrair uma quantidade do item do carrinho.

Tanto a quantidade, como o id do produto, são repassados para a payload da action updateAmount.

Essas funções são chamadas pelos botões de mais e menos.

E entre ambos, aparece a quantidade atual do produto.

Feito isso a nossa aplicação está pronta.

 

Conclusão

Com esse APP, vimos todos os conceitos principais do Redux, como store, action e reducer, além de entender a utilidade dele ao trabalhar com estados globais.

Agora para continuar seus estudos, saiba que o Redux tem outros módulos que se conectam a ele, o que mais indico de início é o saga, que fica responsável por tratar efeitos colaterais dentro do Redux, já que o Reducer só permite funções puras.

https://github.com/redux-saga/redux-saga

Além disso também indico dar uma olhada no método createSlice do Redux Toolkit, que simplifica mais ainda a criação de actions e reducer.

https://redux-toolkit.js.org/api/createSlice

Se você gostou deste artigo e quer mais conteúdo sobre o mundo Javascript deixa um comentário ai em baixo.

Obrigado

 


Primeira vez no OneBitCode? Curtiu esse conteúdo?
O OneBitCode tem muito mais para você!


O OneBitCode traz conteúdos de qualidade, e em português, sobre programação com foco em Ruby on Rails e também JavaScript.
Além disso, aqui sempre levamos à você conteúdos valiosos sobre a carreira de programação, dicas sobre currículos, portfólios, perfil profissional, soft skills, enfim, tudo o que você precisa saber para continuar evoluindo como Programador(a)!

Fique por dentro de todos os conteúdos o/

 

Nossas redes sociais:

📹 • https://youtube.com/Onebitcode [Live todas as terças-feiras às 19h)
💻 • https://linkedin.com/company/onebitcode
🙂 • https://facebook.com/onebitcode
 📱  • https://instagram.com/one_bit_code
🐦 • https://twitter.com/onebitcode

 

Nossos cursos:

🥇 • Programador Full Stack Javascript em 8 Semanas
💎 • Curso Completo de Ruby
 ⚙  • Minicurso: API Rails 5 Completo
🐞 • Minicurso de Testes para Ruby on Rails com RSpec

 

Espero que curta nossos conteúdos e sempre que precisar de ajuda, fala com a gente!
E
stamos aqui para você 🙂

Bem-vindo à família OneBitCode o/

0 0 vote
Article Rating
abril 12, 2020
Subscribe
Notify of
guest
2 Comentários
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Ryan Ricardo
Ryan Ricardo
6 meses atrás

Muito Bom!!!

igor
igor
6 meses atrás

Eu estou recebendo o seguinte erro na função anonima na pagina home TypeError: Cannot read property ‘reduce’ of undefined

Feito com s2 por OneBitCode
2
0
Would love your thoughts, please comment.x
()
x
%d blogueiros gostam disto: