Quem tem medo do Redux?
Sempre que ouço alguém que não conhece o redux, e quer estuda-lo, ouço coisas como:
"Nossa, ouvi dizer que o redux é difícil", ou "Já estou apanhando agora, imagina quando ver o redux" e coisas do tipo.
Realmente, o redux não é algo tão simples de entender, mas também não é esse monstro que parece/dizem.
Primeiramente, pra que serve o redux?
Ao contrário do que parece, (eu pensava que era, inclusive), o redux não precisa ser usado exclusivamente no ReactJS. Comumente você vê projetos ReactJS usando redux, mas ele pode ser usado em qualquer aplicação JS.
Vou tentar ser didático, pra facilitar o entendimento aqui.
Imagine uma empresa que tem um almoxarifado. Todos os materiais da empresa ficam centralizados lá. Cada departamento tem o seu estoque interno, e não podem "emprestar" materiais para outro setor. Quando determinado material acaba, ou não é suficiente, alguém vai até o almoxarifado buscar.
Processo simples, né?
Agora imagine que, você tem uma aplicação, com diversos componentes. Cada componente tem um estado local (state) que não compartilha informações com outros componentes, e que em certos momentos, precisa buscar informações em um repositório central.
Hey, calma aí! Parece familiar:
Departamento -> Componente
Material -> Estado local (state)
Almoxarifado -> Redux
Ok, o conceito é fácil. Vamos evoluir um pouco mais.
Dentro do almoxarifado, o almoxarife é responsável por receber a requisição do material, faz os procedimentos necessários, e a repassa para o estagiário atualizar o estoque dando baixa no material (ou aumentando o estoque em caso de entradas). O material então é entregue no seu departamento.
Na sua aplicação, você faz uma requisição para uma action, que executa uma função. O resultado dela, é repassado para um reducer, que atualiza o estado global.
Almoxarife -> action
Estagiário -> reducer
Legal! Pra complementar, vamos complicar um pouco.
O material que você pediu não está no estoque. Será necessário realizar um pedido de compra no site do fornecedor, e aguardar chegar. Quando for entregue, o estoque será atualizado e seu departamento receberá o material.
Na sua aplicação, você precisa de uma informação que é dada por uma API. Você faz a requisição para a action, a action realizará uma outra requisição para essa API (através de um middleware), e quando a informação voltar, ela irá repassar para o reducer, que atualizará o estado.
Pedido de compra -> Requisição a API
Aguardar chegar -> Promise
Site do fornecedor -> Middleware (Normalmente Redux Thunk ou Saga, mas existem outros).
Viu, não é tão complicado!
Ok. Agora vamos ver um pouco de código.
A ideia é mostrar como podemos criar a nossa store de forma simples, usando o Redux Thunk. Deixaremos o Redux Saga para outro post ;)
Primeiro iremos criar o projeto rodando o comando:
create-react-app teste-redux
Será criada uma estrutura como essa:
Na pasta src, deixe apenas os arquivos App.js e index.js. Exclua o resto. Altere os dois para que fiquem dessa forma:
Se rodarmos o comando npm start, uma página será aberta no navegador mostrando "Teste Redux".
Agora, iremos instalar as dependências para usarmos o redux e o redux thunk.
Rode no terminal:
npm i redux react-redux redux-thunk
Ainda na src, vamos criar 4 arquivos:
- appReducers.js
- store.js
- action.js
- reducer.js
Vamos um por um, nessa ordem:
No action.js, criaremos uma função que retorna um dispatch. Esse cara quem controla é o Redux Thunk. Todo o código de requisição a API precisa estar dentro desse dispatch. No exemplo não vamos consumir API, apenas como exemplo.
Na linha 5, chamamos o método dispatch passando um objeto com 2 parâmetros:
- type: Identificador dessa função. Através desse parâmetro o reducer saberá qual valor do store ele deve atualizar.
- payload: É o valor que queremos passar. Pode ser de qualquer tipo.
No reducer.js, iniciamos uma constante que será o nosso estado inicial. Estou iniciando com um array vazio, chamado materiais.
Na linha 5, chamo uma função que recebe dois parâmetros, um estado, e um action. Caso o estado ainda não exista (quando a aplicação é iniciada), atribuímos o estado inicial para ela.
Na linha 6 fazemos um switch case pra validar qual o tipo de informação estamos lidando. Lembra que passamos na action esse valor como COMPRA_MATERIAL? Pois então, é agora que o reducer vai receber essa informação e verificar o que deve ser feito, que é o que fazemos na linha 8. Estamos atualizando o estado, usando as informações que já existem (fazendo o spread de state), e incluímos esse novo material no array que já temos (também usando o spread).
Caso nenhuma condição satisfaça esse switch case, ele simplesmente retorna o estado.
OBS- É muito importante esse default no final, e já vou explicar por que.
No appReducers.js, iremos unir todos os reducers da nossa aplicação. Nesse exemplo usamos apenas 1, mas em aplicações maiores você terá vários reducers. Repare na linha 4 o método combineReducers. Aqui conseguimos entender porque precisamos de swtich cases nos reducers. Quando uma action chama o dispatch, TODOS os reducers são notificados, mas apenas as condições que você criou terão efeito.
E por último, vamos criar nosso store. Importamos o middleware Redux Thunk e nossos reducers combinados. Na linha 4 importamos 2 caras importantíssimos do pacote redux, que são applyMiddleware e createStore. Os nomes já são auto-explicativos.
O applyMiddleware espera como parâmetro os middlewares que vamos usar (declarado na linha 6).
O createStore espera os nossos reducers combinados, e também nossos middlewares. Por isso na linha 8 já criamos tudo isso com apenas uma instrução.
Por fim, precisamos fazer uma pequena alteração no nosso App.js:
Aqui nós importamos o Provider do react-redux, e nosso store. Como elemento "container", usamos o Provider, passando nosso objeto store como propriedade. Com isso, temos uma aplicação rodando com redux \o/
Execute o comando npm start e veja o resultado.
Não mudou nada! hehe
Mas acredite em mim, isso é bom. Significa que toda nossa store foi criada, o middleware foi aplicado, e temos o redux pronto para usarmos. Vamos fazer isso?
A ideia é apenas criar um botão que irá chamar o método na nossa action, e aí vamos ver a criança em ação.
Vamos mudar a estrutura do nosso App.js. deixe dessa forma:
O que fizemos foi deixar dentro do store apenas um componente que chamamos de Home. Esse será o nosso componente principal. Portanto, na pasta src crie o home.js:
Nas linhas 2 e 4 importamos o método connect do react-redux, e o nosso método compraMateriais, que está na nossa action.
Na linha 8, fazemos a desestruturação para recuperar materiais e compraMateriais, que estão nas props do componente.
Na linha 13 criamos um botão que chama esse método.
Na linha 16, criamos um map para percorrer toda nossa lista de materiais. Exibimos o nome e a quantidade dentro de um <li>
Agora, temos 2 métodos que são responsáveis por recuperar os dados do redux.
Na linha 27 criamos o mapStateToProps, que recupera um valor do store, e atribui para as props desse componente.
O mesmo acontece na linha 31, mas esse recupera os métodos que criamos nas actions.
Por fim, na linha 35 chamamos o método connect, que une os dois em apenas um componente, e no final do arquivo o exportamos.
Com isso teremos a aplicação rodando, e para cada vez que o botão é clicado, um material é adicionado na lista.
Sei que parece complicado de início, mas depois de alguns actions e reducers, as coisas ficam mais claras.
IMPORTANTE- Você NÃO precisa usar TUDO no redux. Você pode continuar usando o state da classe para valores locais, como dados de formulário por exemplo.
Caso queira, o código do projeto pode ser encontrado aqui.
Espero que tenham gostado. Até o próximo artigo ;)