React/Redux Básico

Entenda o básico de React e Redux


Chega um momento no estudo do React em que devemos enfrentar o Redux. Quando eu lidei com isso da primeira vez foi difícil e eu fiquei um tempo confuso, mas com o tempo a sua estrutura foi ficando mais clara, então eu decidi, como material de estudo, criar um esquema. Aos poucos vou melhorando esse esquema para torná-lo mais prático, e acredito que ele possa ajudar outros estudantes do assunto, assim resolvi publicá-lo aqui, seguido de explicações:

Para começar, é preciso importar as bibliotecas do Redux:

É só mandar um:

1.Estrutura de pastas

Uma boa estrutura inicial de pastas para o Redux seria a seguinte:

Essa é apenas uma proposta, e há quem não utilize a pasta store, preferindo utilizar o arquivo index.js direto da pasta redux, por exemplo, ou use os thunks junto das actions

Faça a sua escolha! Busque a estrutura que lhe parecer mais lógica.

2.Responsabilidades

  • Types

Definição de variáveis. Usamos variáveis no lugar de strings para evitar erros de digitação, pois essas variáveis serão utilizadas diversas vezes em nossa aplicação.

  • Actions

Funções criadoras de ações. Aqui são criados objetos que irão passar para o store o valor a ser alterado em nosso estado. Actions são funções puras, e tudo o que elas fazem é montar um objeto que será lido no reducer.

  • Thunk

Eu gosto de separar os thunks das actions, embora visualmente elas pareçam a mesma coisa. Aqui fica a lógica do que deve ser feito como efeito colateral antes de passar os dados para a actionEfeito colateral é o que altera dados fora do escopo da nossa função. Pode ser, por exemplo, a persistência de dados em um banco de dados ou em um arquivo csv.

No exemplo abaixo, a função addProduct recebe um produto por parâmetro, envia esse produto para o servidor, recebe como resposta a lista atualizada de produtos e envia essa lista atualizada para o reducer, utilizando a action setList (observe a diferença de responsabilidades entre actions e thunks)

NB: o axios trata os dados pelo formato correspondente; se for utilizar o fetch será preciso converter os dados para string e para json nas ocasiões apropriadas.

  • Reducers

Os reducers recebem os novos dados e atualizam o estado de maneira correspondente. É aqui que o estado é iniciado e fica armazenado. Normalmente, é “setado” um estado inicial vazio, que pode ser utilizado para posteriormente se limpar o estado.

Aqui é tomada uma ação de acordo com o valor recebido. No nosso exemplo, um novo produto é enviado para o thunk, que atualiza o back-end e retorna a lista atualizada. O thunk então despacha uma action com a nova lista, que é recebida no reducer , e é utilizada para atualizar o estado.

O novo estado vai assumir o valor retornado pelo switch

  • Store

store recebe os valores do reducer e registra o estado de forma a torná-lo disponível em todos os lugares da aplicação.

3. Estrutura mais complexa

Nosso exemplo acima é bem simples, mas aplicações volumosas podem ter várias actionsthunks e reducers, por exemplo para usuários (cadastro, login, alteração de dados…) e produtos no carrinho (adicionar, remover, limpar…) ou outros tipos de dados (mensagens entre usuários, chat com robô para tirar dúvidas, etc). Nesse caso, cada arquivo terá seus dados correspondentes, por exemplo:

Nesse caso, seu index.js deve fazer a junção desses reducers (com as actions e thunks você não precisa se preocupar, pois eles são disponibilizados via import, então a estrutura de pastas não altera sua funcionalidade):

4. Pondo para funcionar

Para que a estrutura acima funcione, temos que disponibilizá-la globalmente em nossa aplicação. fazemos isso no arquivo App.jsx:

O componente Provider recebe nossa store e a disponibiliza para tudo o que estiver contido dentro do componente App – isto é, toda nossa aplicação.

N.B.: Se sua aplicação tem uma estrutura simples, com apenas um reducer, você pode exportar seu único reducer e passá-lo para o Provider como valor de store, pulando o item 3 acima.

5.Usando e atualizando o estado no redux

Uma vez estruturado o estado no seu redux, agora você pode acessá-lo de qualquer lugar da sua aplicação, ou atualizá-lo, por meio dos hooks.

Pronto! Nada de dados passando por props na lógica do nosso carrinho de compras.

O uso do Redux não nos priva de utilizarmos useState ou passar dados por props, mas agora esses recursos ficam circunscritos à lógica interna do componente. A parte pesada da manipulação do estado, com dados que precisam ser acessados de páginas e rotas distintas, é muito bem administrada pelo Redux.

Existem outros hooks e outras formas de fazer uma estrutura parecida com a descrita acima; a documentação do Redux pode ser consultada em https://redux.js.org/, e a do Thunk em https://www.npmjs.com/package/redux-thunk.

Para debugar o redux, eu inseri acima um código que permite a comunicação da sua aplicação com o redux dev tools, que funciona como uma extensão do navegador. Veja a documentação em https://github.com/zalmoxisus/redux-devtools-extension

Algumas considerações:

  • A estrutura acima é apenas uma proposta. Pode não ser a mais prática, mas eu tentei apresentar a estrutura mais lógica e enxuta possível – há quem prefira usar actions e thunks juntos na pasta das actions… isso pode concentrar melhor os conteúdos, mas há uma diferença entre o que cada elemento faz, e isso também determina de que maneira devemos procurar por erros quando alguma coisa quebra no nosso código. Se uma ação despachada da aplicação altera o estado, mas não atualiza o banco de dados, onde você vai procurar o erro? No local onde é feito o fetch, ou seja, em um thunk. Se é o contrário, atualiza o servidor mas não o estado local… bom, aí o problema pode estar em qualquer lugar a partir do retorno do fetch: no thunk, na action posterior ou no reducer que vai tratar essa action.
  • Também é comum ver as types sendo exportadas das actions. Mas, se você parar para pensar, são coisas distintas, e é bonito ver cada coisa no lugar certo.
  • A divisão das actionsreducers e types em pastas e arquivos separados se mostra mais eficaz quando um projeto grande é desenvolvido simultaneamente por várias pessoas. Cada uma delas, na sua branch, vai criar essas pastas, e na hora de fazer merge, dificilmente vai ter algum conflito, pois, no final, o que teremos serão arquivos separados. Pode haver conflito nos pontos de confluência, como no arquivo reducers/index.js, onde haverá o import dos reducers e eles terão que ser passados para o combineReducers. Mas a manutenção disso é muito mais fácil (a princípio é apenas uma linha) do que deixar tudo em um só lugar, inclusive no caso dos arquivos na pasta types.

Na estrutura acima, o único conflito a ser tratado no merge será a linha que contém o export no index de cada pasta:

0

Escreva o primeiro comentário

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *