Iniciando com Redux em 9 passos

Este tutorial será o mais curto e objetivo sobre Redux que você vai encontrar!

Rafael Maruta
React Brasil
Published in
9 min readFeb 14, 2018

--

Boa tarde pessoal! Tudo bem?

Com este artigo pretendo engajar aqueles que estão iniciando com Redux, focando apenas nas partes básicas, usando o mínimo de implementação para que funcione. Recomendo um conhecimento básico de React, JSX, ES6+, e também alguns termos de programação funcional.

Este artigo não contempla:

No final do tutorial deixei o link do repositório com o exemplo funcional. Dividi o tutorial em 9 passos.

Primeiramente explicarei brevemente a arquitetura do Redux

Arquitetura do Redux

O Redux é uma implementação criada pelo Dan Abramov da arquitetura Flux, que foi criada pelo Facebook. Ela soluciona o problema de compartilhamento de estados entre componentes, tornando-o unidirecional. A ilustração a seguir descreve tudo:

À esquerda é o problema da dificuldade de transmissão de estados, e à direita é a solução proposta pelo Redux

Através da ilustração percebemos que o Redux simplesmente simplifica a evolução de estados de uma aplicação quando há múltiplos estados para controlar e muitos componentes que precisam atualizar ou se inscrever nessa evolução, tirando a responsabilidade de cada componente de guardar o estado e passando para uma centralizada e única Store.

Fluxo de uma evolução de estado

Como uma imagem vale mais do que mil palavras, segue abaixo um diagrama que mostra como funciona o fluxo de uma evolução de estado com Redux, com a explicação de cada parte a seguir.

Imagem extraída de https://itnext.io/integrating-semantic-ui-modal-with-redux-4df36abb755c

Para realizar tal fluxo, o Redux depende de 4 partes:

  • Store: é o container que armazena e centraliza o estado geral da aplicação. Ela é imutável, ou seja, nunca se altera, apenas evolui.

Podemos ter apenas uma Store por aplicação, ou seja, ela é a Única Fonte de Verdade (Single Source of Truth).

  • Actions: são fontes de informações que são enviadas da aplicação para a Store. São disparadas pelas Action Creators, que são simples funções que, ao serem executadas, ativam os Reducers.
  • Reducers: recebem e tratam as informações para que sejam ou não enviadas à Store.
  • Conexão dos componentes ao Redux: para poderem se inscrever à evolução de estados da Store ou disparar eventos para evoluí-la.

Pode não ter ficado muito claro agora, mas não precisa se preocupar, pois com a aplicação será mais fácil de entender. Caso queira informações mais detalhadas, recomendo a leitura da documentação oficial:

1. Instale e rode o Create React App

Create React App é um boilerplate criado pelo Facebook para iniciar rapidamente um projeto com React. Ele já vem com algumas dependências e plugins instalados, além de algumas pré-configurações.

Caso você possua npm 5.2+ ou maior, poderá instalar desta forma:

npx create-react-app create-react-redux-app

Se não tiver, é necessário instalar o Create React App globalmente. Rode em seu terminal o comando:

npm i -g create-react-app

… e ao finalizar:

create-react-app create-react-redux-app

O create-react-redux-app é o nome da pasta que será criada. Agora entre na pasta:

cd create-react-redux-app

… e rode com Yarn:

yarn start

ou com npm:

npm start

Uma janela no navegador abrirá, na porta http://localhost:3000/ exibindo:

Print do navegador exibindo a tela inicial do Create React App

2. Adapte a View para o tutorial

Acesse o arquivo create-react-redux-app/src/App.js e substitua a marcação (negritei as mudanças):

import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
return (
<div className="App" style={{ paddingTop: '10px' }}>
<input type='text' />
<button>
Click me!
</button>
<h1>teste</h1>

</div>
);
}
}
export default App;

Ficará parecido com isto:

Bonitão não?

A idéia é que, quando clicarmos no botão Click me!, o texto "teste" seja substituído pelo conteúdo que está no input, via Redux.

3. Instale o Redux e o React Redux

O Redux é a lib de container de estados, enquanto o React Redux realiza a conexão entre React e o Redux.

No terminal no diretório do exemplo onde está o package.json, rode:

Para Yarn:

yarn add --dev redux react-redux

Para npm:

npm i -D redux react-redux

4. Crie a Store

Crie uma pasta na raíz da pasta /src chamada store. Dentro dela, crie um arquivo index.js com o conteúdo:

import { createStore } from 'redux';
import { Reducers } from '../reducers';
export const Store = createStore(Reducers);

Aqui criamos a Store e dizemos quais são os seus respectivos Reducers. Vamos criá-los adiante.

5. Crie o Reducer

Como a Store é a Única Fonte de Verdade, teremos que combinar todos os reducers da aplicação e enviar à Store. Crie uma pasta na raíz da pasta /src chamada reducers. Dentro dela, crie um arquivo index.js com o conteúdo:

import { clickReducer } from './clickReducer';
import { OtherReducer } from './otherReducer';
import { combineReducers } from 'redux';
export const Reducers = combineReducers({
clickState: clickReducer,
otherState: otherReducer
});

Caso sua aplicação possua vários reducers, você pode combiná-los para enviá-los à Store da seguinte forma, usando o método combineReducers.

A chave do objeto é o nome na qual o estado será acessado pela aplicação, enquanto o seu valor é o Reducer, função pura que filtra os dados e que criaremos adiante. Ficará de sua escolha caso prefira usar o mesmo nome para a chave e valor.

Aqui definimos qual será a chave do Reducer na Store quando quisermos acessar o seu estado, além de podermos combinar vários Reducers para serem conectados à Store.

Crie o arquivo clickReducer.js na mesma pasta com o seguinte conteúdo:

const initialState = {
newValue: ''
};
export const clickReducer = (state = initialState, action) => {
switch (action.type) {
case 'CLICK_UPDATE_VALUE':
return {
...state,
newValue: action.newValue
};
default:
return state;
}
};

Este será o nosso Reducer que será acionado pela Action caso o type dela seja CLICK_UPDATE_VALUE, caso contrário manterá o estado atual.

IMPORTANTE: esta função deverá ser pura, ou seja, retornar um novo objeto, pois lembrando, a Store é imutável. Conseguimos preservar o restante do estado usando o ...state, que recupera o estado anterior e passa para o novo objeto.

O código abaixo está sujeito a bugs:

export const clickReducer = (state = initialState, action) => {
switch (action.type) {
case 'CLICK_UPDATE_VALUE':
return state.newValue = action.newValue;
default:
return state;
}
}

Agora vamos criar a Action.

6. Crie a Action e sua Action Creator

Crie uma pasta na raíz da pasta /src chamada actions. Dentro dela, crie um arquivo index.js com o conteúdo:

export const clickButton = value => ({
type: 'CLICK_UPDATE_VALUE',
newValue: value
});

Aqui a função é a Action Creator, e o que ela retorna, que é o objeto, é a Action. Ao ser disparada, ela comunicará ao Reducer que o type é CLICK_UPDATE_VALUE, além do valor newValue: value que deverá ser atualizado na Store. Novamente aqui, caso prefira usar o mesmo nome para chave e valor, ficará de sua escolha.

Os types, que são as Action Types, recomendo separar num arquivo próprio para importarmos apenas as constantes, desta forma, num arquivo:

src/actions/actionTypes.js:

export const CLICK_UPDATE_VALUE = 'CLICK_UPDATE_VALUE';

src/reducers/clickReducer.js atualizado:

import { CLICK_UPDATE_VALUE } from '../actions/actionTypes';const initialState = {
newValue: ''
};
export const clickReducer = (state = initialState, action) => {
switch (action.type) {
case CLICK_UPDATE_VALUE:
return {
...state,
newValue: action.newValue
};
default:
return state;
}
};

src/actions/index.js atualizado:

import { CLICK_UPDATE_VALUE } from './actionTypes';export const clickButton = value => ({
type: CLICK_UPDATE_VALUE,
newValue: value
});

Chegou o esperado momento de fazermos a conexão com os componentes!

7. Crie o Provider para conectar a aplicação à Store

No arquivo src/index.js faça algumas alterações, ele deverá ficar assim (negritei as mudanças):

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import { Store } from './store';
ReactDOM.render(
<Provider store={Store}>
<App />
</Provider>
, document.getElementById('root'));
registerServiceWorker();

Aqui é onde o estado da Store se conectará com toda a aplicação, através do Provider Pattern, que possibilita que a Store seja acessível a todos os componentes abaixo dele. Veja mais sobre em:

8. Conecte o estado da Store ao componente

Para fazer a conexão com a Store, agora devemos atualizar o nosso componente src/App.js:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import './App.css';
class App extends Component {
render() {
const { newValue } = this.props;
return (
<div className="App" style={{ paddingTop: '10px' }}>
<input type='text' />
<button>
Click me!
</button>
<h1>{newValue}</h1>
</div>
);
}
}
const mapStateToProps = store => ({
newValue: store.clickState.newValue
});
export default connect(mapStateToProps)(App);

O método mapStateToProps transforma um trecho do estado da Store em uma prop utilizável pelo componente, com o nome newValue.

Ao acessarmos a chave clickState, que foi definida no arquivo src/reducers/index.js na linha 5:

clickState: clickReducer

… podemos extrair a sua chave newValue, que foi definida no arquivo src/reducers/clickReducer.js na linha 12:

newValue: action.newValue

No momento de exportar o componente, precisamos fazer a sua conexão através do método connect do React Redux, passando o método mapStateToProps como parâmetro e o componente atual como parâmetro na função retornada. Este padrão no React se chama High Order Component.

Ao fazermos esta conexão, podemos usar o newValue como prop no componente:

...
const { newValue } = this.props;
...
...
<h1>{newValue}</h1>
...

Para sabermos se está funcionando, basta trocarmos o initialState no arquivo src/reducers/clickReducer.js na linha 4 e conferirmos se reflete na página:

const initialState = {
newValue: 'Atualizado via Redux!'
};
Nossa tela atualizada, garantindo que a conexão com a Store foi bem sucedida!

9. Disparando um evento para evoluir a Store

Agora vamos criar a funcionalidade de evoluir o estado da Store a partir do preenchimento do input e o clique no botão Click me!. O código deverá novamente ser atualizado conforme a seguir:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { clickButton } from './actions';
import './App.css';
class App extends Component {
state = {
inputValue: ''
}
inputChange = event => {
this.setState({
inputValue: event.target.value
})
}
render() {
const {
clickButton,
newValue
} = this.props;
const { inputValue } = this.state; return (
<div className="App" style={{ paddingTop: '10px' }}>
<input
onChange={this.inputChange}
type='text'
value={inputValue}
/>
<button onClick={() => clickButton(inputValue)}>
Click me!
</button>
<h1>{newValue}</h1>
</div>
);
}
}
const mapStateToProps = store => ({
newValue: store.clickState.newValue
});
const mapDispatchToProps = dispatch =>
bindActionCreators({ clickButton }, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(App);

Criamos o método mapDispatchToProps para converter a Action Creator clickButton que criamos no arquivo src/actions/index.js em uma prop para o componente. O método bindActionCreators facilita este trabalho. Ao clicar no botão Click me!, o valor do state inputValue que foi alterado pelo input text é enviado à Store pelo método clickButton, que foi mapeado no componente como prop.

Por fim, passamos o método mapDispatchToProps como segundo parâmetro do método connect. Agora é só testar!

Funcionou! =D

É importante lembrar que há diversas formas de se disparar eventos à Store, eu abordarei algumas delas em um futuro artigo.

Conclusão

Por enquanto isto é tudo pessoal. Segue o link do repositório com o exemplo:

Caso eu tenha esquecido de algo importante, ou tenha ficado confuso, ou a melhorar, ou vocês tenham dúvidas, algo a acrescentar, por favor deixem nos comentários! Lembrando que não contemplei os Redux Middlewares, que provavelmente ficará para um próximo artigo!

Espero que tenham gostado! E muito obrigado por terem acompanhado!

Recomendação de cursos

--

--