Essa aplicação foi desenvolvida no módulo 07 do curso GoStack/Bootcamp da Rocketseat. O intuito desse módulo foi entender o que é redux e como usar.
$ git clone <https://github.com/mauriani/RocketShoes-Web>
- Depois disso acesse a pasta de cada do projeto e instale a dependências com o seguinte comando abaixo
$ yarn install # ou npm install
- Para dá start na aplicação precisamos deixar a api rodando juntamente com o nosso react.
npx json-server server.json -p 3333
yarn start
Para fazermos o processo de manipulação de uma api usamos JsonServer. Para usar no projeto precisamos criar um arquivo chamado server.json e ao dá start na aplicação a api tem que está rodando também.
Adicionando a biblioteca
yarn global add json-server
Start
npx json-server server.json -p 3333
- React Js
- Redux
- Redux Saga
- Json Server
- Reactotron
- O Redux é uma biblioteca para gerenciamento de estado que segue os princípios da arquitetura flux.
- Redux é um container de estado previsível para aplicações JavaScript.
Em nossa aplicação vamos utilizar o redux para controlar todo o processo de adicionar um item ao carrinho, contabilizar quantos itens vamos ter, e até mesmo o total. Quando instalamos o redux vamos disparar um action e ela será responsável por fazer todo os processo. A configuração do processo está descrito abaixo.
Vamos instalar o redux e pacote de interação entre react e redux.
yarn add redux react-redux
Após instalar criamos uma pasta store com as configurações redux. Assim:
import { createStore } from 'redux';
const store = createStore();
export default store;
Mas precisamos ir no App.js e importar de dentro do redux um carinha chamado provider. Esse provider deixa disponível o store da aplicação para todo o ambiente.
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import Header from './pages/components/Header';
import GlobalStyle from './pages/styles/global';
import Routes from './routes';
import store from './store';
function App() {
return (
<Provider store={store}>
<BrowserRouter>
<Header />
<Routes />
<GlobalStyle />
</BrowserRouter>
</Provider>
);
}
export default App;
- Depois de instalar a biblioteca precisamos criar uma pasta store dentro de src, feito isso foi adicionado um arquivo index.js na raiz.
- Dentro de store temos uma pasta modules e outra para o nosso cart, dentro de cart adicionamos um arquivo chamado reducer.
- Para fazer mais de um redux, na pasta do modules, na raiz, criamos um arquivo chamado rootReducer.
Estrutura:
Arquivos:
- Store/index
import { createStore } from 'redux';
import rootReducer from './modules/rootReducer';
const store = createStore(rootReducer);
export default store;
-
modules/rootReducer
Esse arquivo foi criado com o intuito de se em algum momento desejamos criar outros reducer, para adicionar basta import a pasta e adiciona ao comando de combineReducers.
import { combineReducers } from 'redux';
import cart from './cart/reducer';
export default combineReducers({
cart,
});
- cart/reducer.js
export default function cart() {
}
-
No nosso projeto, tudo partiu da nossa página Home.
-
Primeiro importamos connect para que a conexão fosse estabelecida com redux.
- Assim:
import { connect } from 'react-redux';
-
Feito isso, removemos o export default do inicio da classe e colocamos no rodapé da aplicação a configuração de conexão. Assim:
export default connect()(Home);
- Assim que realizamos a conexão, temos acesso a propriedade chamada dispatch. Esse dispatch e responsável por disparar as actions (responsável por dizer ao redux que quero alterar algo) do redux.
handleAddProduct = (product) => { const { dispatch } = this.props; dispatch({ type: 'ADD_TO_CART', product, }); };
-
Dentro do reducer criamos um switch com um case para ele ouvir apenas a minha action.
- Primeiro iniciamos um state vazio.
- Recebe a action
- Pegamos o produto e colocamos dentro do array.
export default function cart(state = [], action) { /** adicionando elementos dentro, REDUX são imutaveis */ switch (action.type) { /** switch garante que ele vai ouvir a acao */ case 'ADD_TO_CART': /** copia todos produtos e adiciona o action.product */ return [...state, action.product]; default: return state; } }
Após configurar a home, vamos na nossa pasta header e fazer o mesmo processo.
- Primeiro adicionar o connect
import { connect } from 'react-redux';
- exportar no rodapé, nesse caso podemos passar parâmetros.
export default connect((state) => ({ cartSize: state.cart.length, }))(Header);
- Nosso arquivo fica assim:
import React from 'react'; import { Link } from 'react-router-dom'; import { connect } from 'react-redux'; import { MdShoppingBasket } from 'react-icons/md'; import { Container, Cart } from './styles'; import logo from '../../assets/images/logo.svg'; // eslint-disable-next-line react/prop-types function Header({ cartSize }) { return ( <Container> <Link to="/"> <img src={logo} alt="Rocketshoes" /> </Link> <Cart to="/cart"> <div> <strong>Meu carrinho</strong> <span>{cartSize} itens</span> </div> <MdShoppingBasket size={36} color="#fff" /> </Cart> </Container> ); } export default connect((state) => ({ cartSize: state.cart.length, }))(Header);
Iniciando com a instalação das bibliotes.
yarn add reactotron-react-js reactotron-redux
-
Depois disso temos que criar uma pasta de configuração na dentro do nosso src.
- config/ReactotronConfig.js
import Reactotron from 'reactotron-react-js'; import { reactotronRedux } from 'reactotron-redux'; if (process.env.NODE_ENV === 'development') { const tron = Reactotron.configure({ host: '192.168.1.148' }) .use(reactotronRedux()) .connect(); tron.clear(); console.tron = tron; }
- No nosso app.js precisamos importar o nosso arquivo de configuração.
import React from 'react'; import { BrowserRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import './config/ReactotronConfig'; import Header from './components/Header'; import GlobalStyle from './pages/styles/global'; import Routes from './routes'; import store from './store'; function App() { return ( <Provider store={store}> <BrowserRouter> <Header /> <Routes /> <GlobalStyle /> </BrowserRouter> </Provider> ); } export default App;
Mesmo assim precisamos fazer a conexão entre reactotron e redux. Para isso na pasta store/index.js e fizemos as seguintes alterações.
import { createStore } from 'redux'; import rootReducer from './modules/rootReducer'; const enhancer = process.env.NODE_ENV === 'development' ? console.tron.createEnhancer() : null; const store = createStore(rootReducer, enhancer); export default store;
O que é o Redux saga ?
O Redux Saga é uma biblioteca que visa tornar os efeitos colaterais do aplicativo (ou seja, coisas assíncronas, como busca de dados e coisas sujas, como acessar o cache do navegador) mais fáceis de gerenciar, mais eficientes de executar, mais fáceis de testar e melhores no tratamento de falhas.
Redux-saga é um middleware redux, o que significa que esse encadeamento pode ser iniciado, pausado e cancelado a partir do aplicativo principal com ações normais de redux, ele tem acesso ao estado completo do aplicativo redux e também pode enviar ações redux.
- Middleware ele intercepta um action.
Instalação
yarn add redux-saga
Vamos para a pasta src/store/cart e criar um arquivo chamado sagas.js . No sagas vamos criar a chamada a api.
import { call, put, all, takeLatest } from 'redux-saga/effects'; import api from '../../../services/api'; import { addToCartSuccess } from './actions'; // * -> significa um generator ou async function* addToCart({ id }) { // yield - é o generator do await, nossa chamada a api. const response = yield call(api.get, `/products/${id}`); // put dispara uma action yield put(addToCartSuccess(response.data)); } // ouvindo quando a action for adicionada export default all([takeLatest('@cart/ADD_REQUEST', addToCart)]);
Depois disso criamos um arquivo chamado sagas.js na raiz do modules.
import { all } from 'redux-saga/effects'; import cart from './cart/sagas'; export default function* rootSaga() { return yield all([cart]); }
As nossas actions tiverem o nome alterado.
export function addToCartRequest(id) { return { type: '@cart/ADD_REQUEST', id, }; } export function addToCartSuccess(product) { return { type: '@cart/ADD_SUCCESS', product, }; }
Na nossa pagina home a função que é chamada é a request.
Depois disso precisamos configurar o nosso index para identificar o nosso saga.
import { createStore, applyMiddleware, compose } from 'redux'; import createSagaMiddleware from 'redux-saga'; import rootReducer from './modules/rootReducer'; import rootSaga from './modules/rootSaga'; const sagaMiddleware = createSagaMiddleware(); const enhancer = process.env.NODE_ENV === 'development' ? compose(console.tron.createEnhancer(), applyMiddleware(sagaMiddleware)) : applyMiddleware(sagaMiddleware); const store = createStore(rootReducer, enhancer); sagaMiddleware.run(rootSaga); export default store;
-