Redux And ReduxSauce

Reduxsauce is a library that provides concise methods for writing action creators and reducers. These methods allow action types to be mapped explicitly to reducers, and this removes the need to implement a switch-case statement. The reducer logic can then be defined separately from this mapping. As a result, the consolidated redux code becomes highly readable, and much easier to maintain and build upon. Additionally, each reducer function can be exported and thus more easily tested. In the end, all the benefits of the “ducks” design pattern can be maintained, while the lines of code required can be significantly reduced.

What is it? Redux!
“A predictable state container for JavaScript apps”

In other words
Redux is a library to keep data organized and consistent across an app

Illustrative Story Time!

Depositing $$$
1. "I would like to deposit my wads of cash, please"

2. "Great, I can definitely help you!"

3. "Balance: 1 Billion Dollars"


=> We make a request to the teller

=> The teller accepts our cash, counts it, loads it, etc

=> Our bank account reflects any new changes

$$$ in the bank = Data in the app

3 Main Components of Redux
Action – The request made to the teller
Reducer – The teller who interprets our request & follows the proper procedure
Store – Bank vault where all money is stored

Things to note:

  • A law-abiding customer cannot go straight to the vault and take money => Data in the Store cannot be directly changed
  • A customer must make a request that the teller understands => Only specific actions can be properly interpreted
  • In this story, there is only one single bank in the entire world => There is only one store in an app

Redux manages app state, but…
what is State???.
A Plain-Old-Javascript-Object (POJO) that holds our app’s data

Our story: { balance: 1000000000 }

Slightly more realistic:

{
  user: {
    email: "pj@smartlogic.io",
    username: "thepeej",
    groupIds: [1, 5, 48]
  },
  firstTimeLoaded: false,
}

Action

The customer’s request

– A POJO that expresses user’s intent
– Has a required attribute of “type”

{
  type: "DEPOSIT_MONEY",
  payload: 1000000000
}
{ type: "WITHDRAW_TWENTY" }

Reducer

The bank teller

  • A pure JS function
  • Accepts two parameters – State & Action
  • Sets initial state
  • Always returns a new State object
function bankTransactions(state = { balance: 100 }, action) {
  switch (action.type) {
    case 'DEPOSIT_MONEY' :
      return { ... state, balance: state.balance + action.payload}
    case 'WITHDRAW_TWENTY' :
      return {... state, balance: state.balance - 20}
    default :
      return state
  }
}
****

Problem! – A reducer only returns a new state!

let state

function bankTransactions(state = {balance: 100}, action) {
  switch (action.type) {
    case 'DEPOSIT_MONEY' :
      return {...state, balance: state.balance + action.payload}
    case 'WITHDRAW_TWENTY' :
      return {... state, balance: state.balance - 20}
    default :
      return state
  }
}

bankTransactions(state, {type: "WITHDRAW_TWENTY"})
// => { balance: 80 }

bankTransactions(state, {type: "WITHDRAW_TWENTY"})
// => { balance: 80 }

bankTransactions(state, {type: "WITHDRAW_TWENTY"})
// => { balance: 80 }

Store

The Bank Vault

Ties everything together

  • Declares and encapsulates application state
  • getState(): Provides access to application state
  • dispatch(): “Saves” state changes from action/reducer output
  • There should be only one Store in an application

Dispatch

The only way to save state changes
Problem:

let state

bankTransactions(state, {type: "WITHDRAW_TWENTY"})
// => { balance: 80 }

bankTransactions(state, {type: "WITHDRAW_TWENTY"})
// => { balance: 80 }

bankTransactions(state, {type: "WITHDRAW_TWENTY"})
// => { balance: 80 }

Solution:

function dispatch(action) {
  state = bankTransactions(state, action)
}

createStore

Requires a reducer to be passed in

function createStore(reducer) {
  let state
  function dispatch(action) {
    state = reducer(state, action)
  }
  function getState() {
    return state
  }
  dispatch({})
  return {
    dispatch,
    getState
  }
}
( subscribe() & listeners are omitted in example )

createStore
How do we use it?

let store = createStore(bankTransactions)

store.getState()
// => { balance: 100 }

store.dispatch({type: 'DEPOSIT_CASH', payload: 40})

store.getState()
// => { balance: 120 }
store.dispatch({type: 'WITHDRAW_TWENTY'})

store.getState()
// => { balance: 80 }

Reduxsauce

Why use it?
– Keeps Redux code well organized
– Makes code easier to read and maintain
– Allows for cleaner code expandability
– Provides easier testing of Reducers

By what means?
– createReducer()
– createActions()

createReducer

import { createReducer } from 'reduxsauce'

// the initial state of this reducer
const INITIAL_STATE = { balance: 100 }

const depositCash = (state, action) => {
  return {... state, balance: state.balance + action.payload}
}
const withdrawTwenty = (state, action) => {
  return {... state, balance: state.balance - 20}
}

// map our action types to our reducer functions
const HANDLERS = {
  ['DEPOSIT_CASH']: depositCash,
  ['WITHDRAW_TWENTY']: withdrawTwenty
}

export default createReducer(INITIAL_STATE, HANDLERS)

createActions

Now, let’s talk about action creators

A function that returns (‘creates’) an action

function depositMoney(amount) {
  return { type: 'DEPOSIT_MONEY', payload: amount }
}
depositMoney(50)
// => { type: "DEPOSIT_MONEY", payload: 50 }

Putting it all together

import { createReducer, createActions } from 'reduxsauce'

const { Types, Creators } = createActions({
  withdrawTwenty: null,
  depositCash: ['payload']
})

export default Creators

const INITIAL_STATE = { balance: 100 }

const depositCash = (state = INITIAL_STATE, action) => {
  return {... state, balance: state.balance + action.payload}
}

const withdrawTwenty = (state = INITIAL_STATE, action) => {
  return {... state, balance: state.balance - 20}
}

const HANDLERS = {
  [Types.DEPOSIT_CASH]: depositCash,
  [Types.WITHDRAW_TWENTY]: withdrawTwenty
}

export const reducer = createReducer(INITIAL_STATE, HANDLERS)

– Creators are exported for use within mapDispatchToProps()
– Reducer is exported for use within createStore()

Reference

Organizando o Redux com Duck Pattern e Redux Sauce | Diego Fernandes

Leave a Reply

Your email address will not be published. Required fields are marked *