Exemplo de aplicação React com Redux
Nesta documentação, vamos construir um webapp para uma agência de viagens utilizando o React, e o Redux como gerenciador de seus estados.
Projeto já adicionado aos favoritos. Clique aqui para ver todos seus favoritos
Obrigado pela sua avaliação, deixe o seu feedback nos comentários :D
Conhecendo o Redux
O Redux é uma implementação da arquitetura Flux que propõe uma solução ao problema de compartilhamento de estados em aplicações web que consiste em criar um fluxo unidirecional de dados que poderá ser consumido por qualquer parte da aplicação.
Embora tenha sido construído com o React em mente, ele é independente a um framework, ou seja, pode ser usado com o Angular, Vue.js ou o jQuery por exemplo.
O Redux é fortemente inspirado em linguagens funcionais, como o Elm, como as funções puras, que veremos mais à frente. Além disso, ele possui uma tríade de princípios que conheceremos a seguir.
Single source of truth
O princípio um único ponto da verdade serve como uma solução para armazenar informações em um único lugar, chamado store, onde qualquer acesso aos dados armazenados na aplicação deve ser feito através dela. Sendo assim, quando um determinado dado é atualizado, uma notificação é enviada a todos os componentes que foram inscritos para receber essa informação a fim de que eles possam atualizar-se, evitando incoerências de dados em determinadas partes da aplicação.
A Figura 1 exibe um comparativo entre duas aplicações, com e sem Redux. Visualizamos como a store pode facilitar a gestão dos dados quando a aplicação começa a ganhar um porte maior, isso porque a store passa a ser responsável por monitorar as mudanças e notificar a todos os que precisam saber delas. Já uma aplicação sem o Redux é obrigada a desencadear eventos entre vários componentes, tornando-os fortemente acoplados. Isso dificulta a manutenção do projeto, uma vez que um componente pode depender de diversos outros para funcionar.
State is read-only
No Redux todas as informações são salvas como somente leitura, ou seja, imutáveis. Isso significa que para executar alguma alteração em nossa store, essa ação precisa ser feita a partir de uma action, que serve para informar a nossa aplicação que um estado foi modificado gerando um novo estado que substituirá o anterior.
A store conta com alguns métodos importantes:
- o getState() permite que informações armazenadas na store sejam recuperadas. Entretanto esses dados são retornados apenas para a leitura.
- o subscribe() serve para notificar quando uma mudança ocorre na store.
Temos um exemplo prático desses métodos a seguir:
const store = createStore(reducer)
store.subscribe(() =>
console.log('store foi modificada', store.getState())
)
Qualquer mudança na store deve ser feita a partir do objeto action, que possui duas propriedades. A type é obrigatória e contém o tipo da ação que está sendo solicitada. Já a propriedade opcional payload tem o conteúdo que será enviado junto a essa ação, como vemos no exemplo abaixo:
{
type: 'EXCLUI_PACOTE_VIAGEM',
payload: {
id: 1
}
}
No exemplo acima, estamos executando a ação do tipo “EXCLUI_PACOTE_VIAGEM”, que solicita a store que um pacote seja excluído e enviamos junto o payload com os dados que serão necessários para encontrar o mesmo, no caso, o { id: 1 }.
Em alguns casos é necessário enviar propriedades específicas a uma action, para isso podemos utilizar um Action Creators, que são funções simples que retornam um objeto formatado. No exemplo abaixo veremos a prática desse tipo de action:
// actions.js
export const ADD_PACOTE = 'ADD_PACOTE'
/**
* action creator para adicionar pacote
*
* @param id
* @param nome
*/
export const adicionaPacote = (id, nome) => ({
type: ADD_PACOTE,
payload: {
id,
nome
}
})
No exemplo abaixo utilizamos o método dispatch, responsável por enviar a store a ação que será executada. No nosso caso utilizaremos a action creators adicionaPacote, criada no exemplo anterior:
// index.js
...
// dispara o action creator
store.dispatch(adicionaPacote(1, 'Viagem para pernanbuco'))
Changes are made with pure functions
Uma função pura é aquela que não causa efeitos colaterais, ou seja, não muda nenhum estado da aplicação e sempre gera o mesmo resultado ao receber os mesmos argumentos, sendo considerada determinística.
No Redux, para descrever um state que posteriormente será alterado é necessário criar um reducer utilizando uma função pura. Esta é chamada cada vez que uma action é acionada, recebendo o state atual e os valores da action, retornando a evolução daquele estado como resultado.
É importante lembrar que os reducers funcionam como modelos para a store e sempre retornam o mesmo resultado, além de não se basearem em nada que esteja fora de seu escopo para atingir tal resultado, como no exemplo abaixo:
// @flow
import { ADD_PACOTE } from '../actions'
function reducer (state = 0, action: { type: string, payload: any }) {
switch(action.type) {
case ADD_PACOTE:
return state
.filter(pacote => pacote.id !== action.payload.id)
.push(action.payload)
default:
return state
}
}
Considere que o nosso reducer executará a condição do switch ADD_PACOTE, onde é criado um novo state a partir do anterior. A diferença para o state anterior é que ele utiliza o método filter para procurar no estado atual do reducer se existe um item de mesmo id que será inserido através do método push.
Lembrando que o Redux utiliza o conceito de imutabilidade e no exemplo acima é possível perceber que não retornamos o mesmo valor. Na verdade será retornado um novo resultado, que é construído a partir do state junto com a action, passados como argumentos para o reducer.
Confira a seguir como ficará o projeto pronto ao final do curso: