When we look at a bit of Redux code, which part is Redux and which is ES6?
In this post, I'll explain ES6 features that are commonly seen with Redux code and explain the difference between the two.
In a blog post about his struggle to learn React, Brad Frost highlighted a common issue when learning React:
"Because React and ES6 are very much intertwined, I have a hard time digesting any given block of code. What’s coming from React? What’s just ES6 JavaScript?"
-- Brad Frost, My Struggle to Learn React
Even simply knowing what is what is tremendously helpful in determining how to learn - it helps you pick references, craft search terms, and focus your learning.
In a previous post, I broke down a lot of the syntax found in React tutorials and examples, showing examples, identifying where it was coming from, and linking to resources for learning.
In this post, I'll do the same for another part of the React ecosystem that has the same problem: Redux.
Redux tutorials and even the Redux documentation contain tons of ES6 conventions, frequently leading to confusion for newer developers or those coming from older JavaScript background. For each feature below I show an example of what it looks like, identify where it is coming from, give a quick overview, and link to learning resources.
Actions and Action Creators (Redux)
// Action
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
// Action Creator
function addTodo(text) {
return {
type: ADD_TODO,
text: text,
}
}
Actions are the Redux mechanism for triggering changes. You can think of them as events with data, that other parts of your Redux application react to. Fundamentally they are just JavaScript objects, but they typically have a standardized structure across your entire application.
Action Creators are simply functions that return actions. They are convenient utilities to return actions, nothing more.
There is no magic or custom functionality in actions and action creators - they are pure JavaScript objects and functions, but they are an important conceptual building block for building a Redux application.
Object Literal Property Shorthand (ES6)
The previous action creator example shows up in the Redux docs as:
function addTodo(text) {
return {
type: ADD_TODO,
text,
}
}
This is taking advantage of the Object Literal Property Shorthand which allows you to shorten a key/value pair in an object to just the key if the key and value have the same name.
Reducers (Redux)
function todoApp(state, action) {
if (typeof state === 'undefined') {
return initialState
}
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
default:
return state
}
}
Reducers are JavaScript functions of a particular format. They accept a state object and an action object, and return a new state object.
These functions once again contain no magic, but they do have a restriction: They must be pure functions. This means that they must not modify their initial arguments, and the value they return must be purely determined by the arguments passed to them.
Default Parameters (ES6)
function todoApp(state = initialState, action) {
return state
}
ES6 added the ability to specify default parameters to functions.
These are values for the function that are assigned if (and only if) the parameter is set to undefined.
Redux examples frequently use this to handle initial state values. If you don't, you will need to handle undefined
values of state
in the body of your function.
Spread/Rest (ES6)
switch (action.type) {
case SET_VISIBILITY_FILTER:
return {...state, visibilityFilter: action.filter }
default:
return state;
}
The spread operator aka the ...
operator, takes an array or an object and expands it into its set of items. This lets you merge objects or create shallow copies just like Object.assign, but with a very tight syntax.
combineReducers (Redux)
import { combineReducers } from 'redux'
const todoApp = combineReducers({
visibilityFilter,
todos
})
An extremely common pattern in Redux is to split out separate reducers for different "slices" of state. These slices are typically parts of the state tree that don't depend on one another, and are then handled by independent reducers, which are then composed together into a single reducer.
This pattern is so common that the combineReducers
method was created to make it simple. You can read more about this in the Redux documentation on splitting reducers.
Store (Redux)
Redux store
import { createStore } from 'redux'
import todoApp from './reducers'
import { addTodo } from './actions'
const store = createStore(todoApp)
const unsubscribe = store.subscribe(() =>
console.log(store.getState())
)
// Dispatch some actions
store.dispatch(addTodo('Learn about actions'))
The store is the fundamental "clearing house" of a Redux application. It provides a container for the state tree, and a manager that connects reducers and actions to each other and calls them all as necessary.
The store provides the methods dispatch
, subscribe
, and getState
, so if you see these being called on an object, there is a good chance it is a Redux store.
Middleware (Redux)
import { createStore, applyMiddleware } from 'redux'
import todos from './reducers'
import { logger } from './middleware'
const store = createStore(
todos,
['Use Redux'],
applyMiddleware(logger)
)
Redux Middleware is something that is far simpler to use than it is to understand. If you're familiar with server-side middleware it is pretty similar - fundamentally, it allows you to insert functions that run before and around your Redux dispatches.
Middleware is an important escape hatch for all sorts of functionality. Where reducers must be synchronous and pure (not looking at global state, not making API calls, etc) middleware can do all of that. You can use middleware to call APIs, persist data to localStorage and much much more.
Wrapping Up
Redux has a bit of a learning curve, but enables a dramatic improvement in architecture for complex front-end applications. That learning curve is worsened for many long-time developers by the entanglement of new JavaScript constructs along with Redux constructs.
This post attempts to flatten that learning curve a little bit by giving you a resource to disentangle Redux from ES6, but it is not intended to teach you all about Redux.
If you're looking to learn more about Redux, here are some recommended resources: