Redux From Scratch (Chapter 2 | Practicing the Basics)

Michael Mangialardi
Coding Artist
Published in
9 min readJul 3, 2017

Buy the Official Ebook

If you would like to support the author and receive a PDF, EPUB, and/or MOBI copy of the book, please purchase the official ebook.

Prerequisites

Chapter 1 | Core Concepts

Scope of This Chapter

So far, we have discussed the core concepts of Redux in order to understand what it does and why it’s useful. The core concepts we discussed were stores, actions, and reducers. In this chapter, we are going to get into the specifics of these concepts and code a very simplistic counter app in order to practice the basics of Redux.

Unpacking the Core Concepts

Stores

As we previously mentioned, a fundamental principle behind Redux is to move states into a container called store. There is only one store per application.

Since the stores contain the states in an application, it has to allow access to a state and allow a state to be updated.

Access is granted to access a state using getState() .

Access is granted to update a state using a dispatched action: dispatch(action) .

There is also another responsibility that falls under a store. It handles registering and unregistering a subscription using subscribe() . The subscription is used to call a function when the state has been updated through a dispatched action.

In order to start coding a store, we need to understand actions and reducers. Let’s discuss these now.

Actions

Actions are dispatched in order to update a state. Let’s unpack actions in more detail.

There are 3 steps in order to update a state via an action:

  1. Define an action
  2. Create the action for dispatch
  3. Dispatch the action

Recall, a reducer will take an incoming dispatched action and apply an update as it requests. We will get to reducers shortly.

Also, note that these steps are listed in a logical order and not temporal order.

Defining an Action

An action is just a plain JavaScript object that contains a type property and properties for the data that is being updated.

For example, let’s say we had a state that contained a message and we wanted to update the message. We would create the following action:

const UPDATE_MSG = 'UPDATE_MSG'{
type: UPDATE_MSG,
message: "Howdy! I'm a Redux action to update a message :)"
}

In the code above, the type is a string stored in a constant variable called UPDATE_MSG .

The reason for specifying a type will make more sense when we look at reducers in just a bit. You can think of the type as a key to help the reducers distinguish the actions being dispatched.

Create the Action for Dispatch

Now, the action is technically just the plain JavaScript object we showed above. However, this action will be within an action creator. An action creator is a function that returns an action when called. Therefore, it “creates” an action.

Let’s take our action from above and show it within an action creator:

function updateMsg() {
const UPDATE_MSG = 'UPDATE_MSG'

return {
type: UPDATE_MSG,
message: "Howdy! I'm a Redux action."
}
}

We will also frequently need to pass in a parameter to use as the updated value. Here’s an example:

function updateMsg(message) {
const UPDATE_MSG = 'UPDATE_MSG'
return {
type: UPDATE_MSG,
message
}
}

Note that we don’t have to write the message line like this: message: message .

Dispatch the Action

We call the action creator function at the dispatch:

store.dispatch(updateMsg("Howdy! I'm a Redux action."))

The action is therefore created when updateMsg is called. Hence, why we think of the actions as being “created” by these functions.

The code above creates a defined by calling the action creator and then dispatches the created action to a store.

Reducers

We should be able to see how actions are defined, created, and dispatched. Now, we need to see how these dispatched actions are handled by reducers.

As we mentioned in the previous chapter, all reducers are pure functions. Meaning, we take the previous state and apply the specified action to create a new state. We do not mutate the previous state.

Think of it like this:

(previousState, action) => newState

React updates a state without mutating the previous state:

this.setState((prevState) => {
return {counter: prevState.counter + 1};
});

With Redux, we will be replacing setState API with our reducer functions.

Let’s piece together an entire reducer function.

First, we have shell of the reducer for our application:

function msgDisplay() {


}

Each reducer will be passed in the targeted state to update and the action to be applied. It will then return a final state. Therefore, let’s add the following parameters and return statement:

function msgDisplay(state, action) {  return state
}

Right now, our returned state is not defined so let’s define an initial state:

const initialState = {
message: ""
}
function msgDisplay(state, action) { return state
}

We can neatly apply the initialized state using the following ES6 magic:

const initialState = {
message: ""
}
function msgDisplay(state = initialState, action) { return state
}

Next, we need to add a switch statement so we can update the state depending on the type of incoming action:

const initialState = {
message: ""
}
function msgDisplay(state = initialState, action) {
switch(action.type) {
case 'UPDATE_MSG':
//update code here
default:
return state.message
}
}

Now, we should be able to see why it was important to define a type property when defining our action in a JavaScript object:

{
type: UPDATE_MSG,
message: "Howdy! I'm a Redux action."
}

The final step is to return a new state with the update specified in our action:

function msgDisplay(state = initialState, action) {
switch(action.type) {
case 'UPDATE_MSG':
const newState = Object.assign({}, state, {
message: action.message
})
return newState.message
default:
return state.message
}
}

Woah! That Object.assign stuff looks a little weird. Let’s break it down.

Object.assign is used to create a copy of an existing object.

We have the empty new object as the first parameter: {}

The second parameter is the state which we want to copy for the new object: state

The final parameter is what we want to update from the previous object for our new object:

{
message: action.message
}
//action.message is "Howdy! I'm a Redux action."

The value of the new state created by the Object.assign stuff is stored into an immutable variable called newState .

newState is an object, so to return the new message, we do: return newState.message .

Putting It All Together

Let’s put our example together and test it to make sure it is working.

We will code this via Codepen. Create an account if you don’t already have one and then fork this template which has Babel and Redux already configured.

Here are the steps which we will complete in order:

  • Define an initial state
  • Define a reducer with initialized static and logic to handle action
  • Initialize store
  • Define an action within an action creator
  • Render value of state to DOM
  • Subscribe to render
  • Dispatch an action (update message after 5 seconds)

For the first two steps, we define an initial state and a reducer which returns the initialized state by default and an updated message on a dispatched action of type 'UPDATE_MSG':

//define an initial state
const initialState = {
message: "Initial Message"
}
//define a reducer with an initalized state
function msgDisplay(state = initialState, action) {
switch(action.type) {
case 'UPDATE_MSG':
const newState = Object.assign({}, state, {
message: action.message
})
return newState.message //"Howdy! I'm a Redux action."
default:
return state.message //Initial Message
}
}

The next step is to initialize the store with a passed in reducer:

//intialize store
let store = Redux.createStore(msgDisplay)

Note that we used Redux.createStore() .

Then, we define an action within an action creator:

//define an action within an action creator
function updateMsg() {
const UPDATE_MSG = 'UPDATE_MSG';
return {
type: UPDATE_MSG,
message: "Howdy! I'm a Redux action."
}
}

This action will update the message to “Howdy! I’m a Redux Action.”

Next, we render the value of the state to the DOM:

//render value of state to DOM
let valueTarget = document.getElementById('value')
function render() {
valueTarget.innerHTML = store.getState().toString()
}
render()

Now, we want subscribe to the store and call render on a change of the state:

//subscribe to render
store.subscribe(render)

To test that our code is working, we can dispatch an event after a 5-second delay:

//dispatch an action (update message after 5 seconds)
setTimeout( () => {
store.dispatch(updateMsg())
}, 5000)

Because we have store.subscribe(render) , the new message will be rendered after the dispatched action is processed by the reducer:

Awesome! We have completed our first Redux app.

Final Code: http://bit.ly/2sj519P

Even though it is not too complex conceptually, this probably still feels a bit awkward. Let’s get some more practice by creating a simple counter app.

Counter App

Start by forking this template via Codepen.

In this pen, we are starting off with a bit more HTML:

<div>
<p>
Clicked: <span id="count">0</span> times
<button id="increment">+</button>
<button id="decrement">-</button>
</p>
</div>

We have a count of the number of clicks, an increment button, and decrement button being displayed.

Let’s write the code in our JavaScript column so the buttons can increment and decrement the count value.

First, we define an initial state:

//define an initial state
const initialState = 0

This is a bit different than the previous example. In the previous example, we had the initial state be an object and the action would update this object. For our counter, the state will simply be a number that we can increment or decrement.

Next, let’s define a reducer with the initialized state and logic to handle an increment or decrement action type:

//define a reducer with an initalized state and logic to handle action
function counter(state = initialState, action) {
switch(action.type) {
case 'INCREMENT':
const incremented = state
return incremented + 1
case 'DECREMENT':
const decremented = state
return decremented - 1
default:
return state // 0
}
}

There is no need to do Object.assign because our state is simply a number and not an object. We copy the current state into a new variable and increment or decrement its value.

Next, let’s initialize our store:

//intialize store
let store = Redux.createStore(counter)

Now, we will define our actions within two action creators:

function increment() {
const INCREMENT = 'INCREMENT';
return {
type: INCREMENT
}
}
function decrement() {
const DECREMENT = 'DECREMENT';
return {
type: DECREMENT
}
}

After that, let’s write the code to render our count value:

//render value of state to DOM
let countTarget = document.getElementById('count')
function render() {
countTarget.innerHTML = store.getState()
}

We also want to subscribe for changes to the store and call the render function when changes occur:

//subscribe to render
store.subscribe(render)

The final step is to add the event listeners so the increment and decrement actions are dispatched on the click of the buttons:

//dispatch actions on event listeners of clicks
document.getElementById('increment')
.addEventListener('click', function () {
store.dispatch(increment())
})
document.getElementById('decrement')
.addEventListener('click', function () {
store.dispatch(decrement())
})

Let’s test it out!

Perfect!

Final Code: http://bit.ly/2ta72Z0

Concluding Thoughts

At this point, we have dipped into Redux with some simple examples. We also have written in vanilla JavaScript. Redux is framework agnostic, however, we will pair it with React in the next chapter.

Chapter 3

Chapter 3 is now available.

Buy the Official Ebook

If you would like to support the author and receive a PDF, EPUB, and/or MOBI copy of the book, please purchase the official ebook.

Cheers,
Mike Mangialardi

--

--