In other words,
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
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
{
user: {
email: "pj@smartlogic.io",
username: "thepeej",
groupIds: [1, 5, 48]
},
firstTimeLoaded: false,
}
{ balance: 1000000000 }
Our story:
Slightly more realistic:
The customer's request
{ type: "WITHDRAW_TWENTY" }
{
type: "DEPOSIT_MONEY",
payload: 1000000000
}
The bank teller
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
}
}
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 }
The Bank Vault
let state
bankTransactions(state, {type: "WITHDRAW_TWENTY"})
// => { balance: 80 }
bankTransactions(state, {type: "WITHDRAW_TWENTY"})
// => { balance: 80 }
bankTransactions(state, {type: "WITHDRAW_TWENTY"})
// => { balance: 80 }
Problem:
function dispatch(action) {
state = bankTransactions(state, action)
}
Solution:
dispatch({type: "WITHDRAW_TWENTY"})
// => { balance: 80 }
dispatch({type: "WITHDRAW_TWENTY"})
// => { balance: 60 }
dispatch({type: "WITHDRAW_TWENTY"})
// => { balance: 40 }
function createStore(reducer) {
}
let state
function getState() {
return state
}
return {
dispatch,
getState
}
function dispatch(action) {
state = reducer(state, action)
}
dispatch({})
( subscribe() & listeners are omitted in example )
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 }
How do we use it?
(This is a terrible logo)
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)
import { createReducer } from 'reduxsauce'
const INITIAL_STATE = { balance: 100 }
const depositCash = (state, action) => {
return {... state, balance: state.balance + action.payload}
}
export const withdrawTwenty = (state, action) => {
return {... state, balance: state.balance - 20}
}
const HANDLERS = {
['DEPOSIT_CASH']: depositCash,
['WITHDRAW_TWENTY']: withdrawTwenty
}
export default createReducer(INITIAL_STATE, HANDLERS)
Initial state set outside of function definition
State manipulation organized into separate functions
Concise routing of action types to business logic
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
}
}
Original Reducer
Eliminates use of switch/case statement which can become messy
import { createActions } from 'reduxsauce'
const { Types, Creators } = createActions({
withdrawTwenty: null,
depositCash: ['payload']
})
Keys of object passed in will become keys/values of the Types after being converted to SCREAMING_SNAKE_CASE
Types
// => { DEPOSIT_CASH: "DEPOSIT_CASH", WITHDRAW_TWENTY: "WITHDRAW_TWENTY" }
function depositMoney(amount) {
return { type: 'DEPOSIT_MONEY', payload: amount }
}
depositMoney(50)
// => { type: "DEPOSIT_MONEY", payload: 50 }
A function that returns ('creates') an action
import { createActions } from 'reduxsauce'
const { Types, Creators } = createActions({
withdrawTwenty: null,
depositMoney: ['payload']
})
Creators.withdrawTwenty()
// => {type: "WITHDRAW_TWENTY"}
Creators.depositMoney()
// => {type: "DEPOSIT_MONEY"}
The keys of the object are also used as the keys (as-is) of Creators. The value is a function which returns an action (aka an "action creator")
(Remember, an action is a POJO with at least a type attribute.)
Creators.depositMoney(30)
// => {type: "DEPOSIT_MONEY", payload: 30}
Creators.depositMoney(30, 50)
// => {type: "DEPOSIT_MONEY", payload: 30}
Creators.withdrawTwenty(30)
// => {type: "WITHDRAW_TWENTY"}
strings become additional action attribute
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()