Intro to Redux
Intro
Redux is a Javascript library that helps us manage state in more complicated applications. It is not designed to work exclusively with React, although it does integrate very well. It can be used on it’s own or with other JavaScript applications.
For the following samples to work you will of course have to install redux, so take care of that if you are following along.
Redux Cycle
Action
An object that contains information about how we want to change some data within our central state.
An action will contain a type and a payload.
Action Creator
A function that creates an action object. Each action will contain unique information, but the format of each action should be the same. So when we create the Action Creator we will be writing out a schema of the action.
Let us take an example where we are creating actions that update the state of several insurance policies and their claims.
const createPolicy = (name, amount) => {
return {
type: "CREATE_POLICY",
payload: {
name: name,
amount: amount,
},
}
}
const deletePolicy = name => {
return {
type: "DELETE_POLICY",
payload: {
name: name,
},
}
}
const createClaim = (name, amountOfMoneyToCollect) => {
return {
type: "CREATE_CLAIM",
payload: {
name: name,
amountOfMoneyToCollect: amountOfMoneyToCollect,
},
}
}
Note that we are just using an insurance company tracking their policies and claims as an example here to understand the Redux process.
Dispatch
The dispatch decides which reducer the action needs to be sent to. The action TYPE is what informs the dispatcher of which reducer it should route the action to.
Reducers
This covers the process of creating array based reducers. If you are looking object based reducers see here: Ncoughlin: React-Redux: Objected Based Reducers
The reducers are going to do different things with the action depending on what our intentions are.
If Correct Action Type, Return New Array
Each reducer is a function that takes two arguments, always in the same order. The first is the current list of actions that have already been saved with that type, and the second is the new action. The output of this reducer is a new array that includes the new action.
// reducers
const claimsHistory = (oldListOfClaims, action) => {
if (action.type === "CREATE_CLAIM") {
// we care about this action
return [...oldListOfClaims, action.payload]
}
}
ES2015 Array Shorthand …
The ...
notation creates a new array starting with the named array, and then adds the additional items to the array.
Babel: Learn ES2015, Default + Rest + Spread
So we have checked to see if the action is the correct type of action to be sending to this reducer, if it is then we send the action to the repository of this type.
Additional Reducer Data Manipulation Strategies
Because it is important to always be sending a new array/object to the store instead of modifying the current one, the strategies we must use to manipulate the data are different from what you would first think. Review these tables for a list of strategies that create a new array/object and then perform the desired action.
Array Manipulation
Bad | Good | |
---|---|---|
Removing an element from an array | state.pop() | state.filter(element => element !== 'hi') |
Adding an element to an array | state.push('hi') | [...state, 'hi'] |
Replacing an element in an array | state[0] = 'hi' | state.map(el => el === 'hi' ? 'bye' : el) |
Object Manipulation
Bad | Good | |
---|---|---|
Updating a property in an object | state.name = 'Sam' | {...state, name: 'Sam'} |
Adding a property to an object | state.age = 30 | {...state, age: 30 } |
Removing a property from an object | delete state.name | {...state, age: undefined } |
" | " | _.omit(state, 'age') |
Return Original Array If Incorrect Action Type
Lastly if we have passed in the wrong type of action we want to simply return the list of claims as it was.
// reducers
const claimsHistory = (oldListOfClaims, action) => {
if (action.type === "CREATE_CLAIM") {
// we care about this action
return [...oldListOfClaims, action.payload]
}
// we don't care about this action.
return oldListOfClaims
}
Default Value For First Action
We need to set a default value for the old array in case this is the first action of its type, in which case requesting that array would return undefined.
// reducers
const claimsHistory = (oldListOfClaims = [], action) => {
if (action.type === "CREATE_CLAIM") {
// we care about this action
return [...oldListOfClaims, action.payload]
}
// we don't care about this action.
return oldListOfClaims
}
Full Reducers Example
Continuing with our example of creating reducers for an mock insurance company. Above we made the reducer that handles adding a claim to the claim list. Now let’s add several more examples.
Below we will also handle what is happening in our accounting department, whether we are gaining money for a new policy or paying out money for a claim. And also the ability to add or remove policies.
Note that in all these examples we are returning a new array. In reducers we never modify the existing array. We always return a new array.
// REDUCERS
// create an insurance claim
const claimsHistory = (oldListOfClaims = [], action) => {
if (action.type === "CREATE_CLAIM") {
// we care about this action
return [...oldListOfClaims, action.payload]
}
// we don't care about this action.
return oldListOfClaims
}
// does the action affect our money balance? Default balance of $100
const accounting = (currentBalance = 100, action) => {
if (action === "CREATE_CLAIM") {
return currentBalance - action.payload.amountOfMoneyToCollect
} else if (action.type === "CREATE_POLICY") {
return currentBalance + action.payload.amountOfMoneyToCollect
}
return bagOfMoney
}
// if new policy add to policy list
// if removing, create new list without payload.name
const policies = (listOfPolicies = [], action) => {
if (action.type === "CREATE_POLICY") {
return [...listOfPolicies, action.payload.name]
} else if (action.type === "DELETE_POLICY") {
return listOfPolicies.filter(name => name !== action.payload.name)
}
return listOfPolicies
}
State
An object that serves as the central repository of all data from the reducers.
Redux Store
Now that we have created all of our action schemas and our reducer functions, we tie all of these things together using a few Redux methods, to create the one all powerful object called store, where the current value of all of our states are stored inside of their objects (action schemas).
// wiring reducers together with store
// pull methods out of Redux library
const { createStore, combineReducers } = Redux
// combining reducers
const ourDepartments = combineReducers({
accounting: accounting,
claimsHistory: claimsHistory,
policies: policies,
})
// create store, pass in ourDepartments
const store = createStore(ourDepartments)
// store object now represents our entire Redux application
// now if we create an action
const action = createPolicy("Alex", 20)
// use Redux dispatch method to run action through reducers
store.dispatch(action)
console.log(store.getState())
Comments
Recent Work
Basalt
basalt.softwareFree desktop AI Chat client, designed for developers and businesses. Unlocks advanced model settings only available in the API. Includes quality of life features like custom syntax highlighting.
BidBear
bidbear.ioBidbear is a report automation tool. It downloads Amazon Seller and Advertising reports, daily, to a private database. It then merges and formats the data into beautiful, on demand, exportable performance reports.