React, Redux & Redux Saga

Redux

Redux is  a predictable state container for JavaScript app

Redux

actions


function getData() {
  return {
    type: FETCHING_DATA
  }
}

function getDataSuccess(data) {
  return {
    type: FETCHING_DATA_SUCCESS,
    data,
  }
}

function getDataFailure() {
  return {
    type: FETCHING_DATA_FAILUR
  }
}

Redux

reducers


const initialState = {
  data: [],
  dataFetched: false,
  isFetching: false,
  error: false,
}

function dataReducer(state = initialState, action) {
  switch(action.type) {
    case FETCHING_DATA:
      return {
        ...state,
        isFetching: true,
      }
    case FETCHING_DATA_SUCCESS:
      return {
        ...state,
        isFetching: false,
        data: action.data,
      }
    case FETCHING_DATA_FAILURE:
      return {
        ...state,
        isFetching: false,
        error: true,
      }
  }
}

Redux

create-react-app ReactRedux

cd ReactRedux

yarn add redux react-redux

Redux

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';

import { Provider } from 'react-redux'
import configureStore from './configureStore'
const store = configureStore()

const ReduxApp = () => (
  <Provider store={store}>
    <App />
  </Provider>
)

ReactDOM.render(
  <ReduxApp />,
  document.getElementById('root')
);

index.js

Redux

create redux files

Reducers folder

  • index.js
  • dataReducer.js

configureStore.js

constants.js

actions.js

 

Redux

Reducers - index.js

import { combineReducers } from 'redux'
import appData from './dataReducer'

const rootReducer = combineReducers({
    appData
})

export default rootReducer

Redux

Reducers - dataReducer.js

import { FETCHING_DATA, FETCHING_DATA_SUCCESS, FETCHING_DATA_FAILURE } from '../constants'
const initialState = {
  data: [],
  dataFetched: false,
  isFetching: false,
  error: false,
}

export default function dataReducer(state = initialState, action) {
  switch(action.type) {
    case FETCHING_DATA:
      return {
        ...state,
        data: [],
        isFetching: true,
      }
    case FETCHING_DATA_SUCCESS:
      return {
        ...state,
        isFetching: false,
        data: action.data,
      }
    case FETCHING_DATA_FAILURE:
      return {
        ...state,
        isFetching: false,
        error: true,
      }
    default:
      return state
  }
}

Redux

actions.js

import { FETCHING_DATA, FETCHING_DATA_SUCCESS, FETCHING_DATA_FAILURE } from './constants'


export function getData() {
  return {
    type: FETCHING_DATA
  }
}

export function getDataSuccess(data) {
  return {
    type: FETCHING_DATA_SUCCESS,
    data,
  }
}

export function getDataFailure() {
  return {
    type: FETCHING_DATA_FAILURE
  }
}

export function fetchData() {}

Redux

configureStore.js

import { createStore } from 'redux'
import app from './reducers'

export default function configureStore() {
  let store = createStore(app)
  return store
}

Redux

constants.js

export const FETCHING_DATA = 'FETCHING_DATA'
export const FETCHING_DATA_SUCCESS = 'FETCHING_DATA_SUCCESS'
export const FETCHING_DATA_FAILURE = 'FETCHING_DATA_FAILURE'

Redux

app.js

import { connect } from 'react-redux'
import { fetchData } from './actions'

import connect  from redux + reducer

Text

function mapStateToProps (state) {
  return {
    appData: state.appData,
  }
}

function mapDispatchToProps(dispatch) {
  return {
    fetchData: () => dispatch(fetchData())
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App)

attach actions and props to the component

Redux

app.js

console.log('props:', this.props);

Redux

create fake api (api.js)

Text

const people = [
  { name: 'Nader', age: 36 },
  { name: 'Amanda', age: 24 },
  { name: 'Jason', age: 44 }
]

export default () => {
  return new Promise((resolve, reject) => {
     setTimeout(() => {
      return resolve(people)
    }, 4000)
  });
}

Redux

thunk middleware

yarn add redux-thunk

Redux

update configureStore.js

import { createStore, applyMiddleware } from 'redux'
import app from './reducers'
import thunk from 'redux-thunk';

export default function configureStore() {
  let store = createStore(app, applyMiddleware(thunk))
  return store
}

Redux

update actions.js

import getPeople from '.api'

export function fetchData() {
  return (dispatch) => {
    dispatch(getData())
    getPeople()
      .then((data) => {
        dispatch(getDataSuccess(data))
      })
      .catch((err) => console.log('err:', err))
  }
}

Redux

update app.js

{
  this.props.appData.isFetching ? <p>Loading</p> :
  <p onClick={() => this.props.fetchData()}>Fetch Data</p>
}
{
  this.props.appData.data.length ? (
    this.props.appData.data.map((person, i) => {
      return <div key={i}>
        <p>Name: {person.name}</p>
        <p>Age: {person.age}</p>
      </div>
    })
  ) : null
}

Redux Saga


yarn add redux-saga

Redux Saga

import { FETCHING_DATA, FETCHING_DATA_SUCCESS, FETCHING_DATA_FAILURE } from './constants'
import { put, takeEvery } from 'redux-saga/effects'
import getPeople from './api'

function* fetchData(action) {
   try {
      const data = yield getPeople()
      yield put({ type: FETCHING_DATA_SUCCESS, data });
   } catch (e) {
      yield put({type: FETCHING_DATA_FAILURE });
   }
}

function* dataSaga() {
  yield takeEvery(FETCHING_DATA, fetchData);
}

export default dataSaga;

saga.js

Redux Saga

import { createStore, applyMiddleware } from 'redux'
import app from './reducers'

import createSagaMiddleware from 'redux-saga'
import dataSaga from './sagas'

const sagaMiddleware = createSagaMiddleware()

export default function configureStore() {
  const store = createStore(app, applyMiddleware(sagaMiddleware))
  sagaMiddleware.run(dataSaga)
  return store
}

configureStore.js

Redux Saga

import { FETCHING_DATA } from './constants'

export function fetchData() {
  return {
    type: FETCHING_DATA
  }
}

actions.js

React + Redux Saga

By Nader Dabit

React + Redux Saga

A basic implementation of redux saga

  • 2,416