Introduction to Redux

Introduction to Redux

Redux is a state container that we are using in some of our React projects at work. As part of my continuous learning I did some basic investigation into what Redux is and how it plays with React.

The Redux site tells us:

Redux is a predictable state container for JavaScript apps.

It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.

You can use Redux together with React, or with any other view library.  
It is tiny (2kB, including dependencies).  

This sounds pretty great.

Further reading explains that Redux doesn't mutate the state directly but instead you modify actions (plain objects). These actions have a specified transformation on the state that are specified within the reducer function.

Redux uses a single store with a single root reducing function. This reducing function can be split into smaller reducers (much like a React root component is split into smaller components). This is the parallel that makes Redux and React a match made in CS heaven.

Basic Redux Concepts

Explain Redux like I'm Five

One of the great collections on dev.to is the #explainlikeimfive tags. The first response in the Explain Redux like I'm Five post is a great introduction to the core concepts of Redux (store, actions, reducers, subscriptions) in a basic analogy.

Getting Started with Redux

The Redux Docs also link to the Getting Started with Redux course that was created by Dan Abramov (the creator of Redux). This 2 hour short course explains not only the best practices but also the concepts behind the creation of Redux.

Getting Started with Redux - egghead.io course

My main takeaways from this course:

  • Each action requires a type property. This type property cannot be undefined.
  • The reducer must be a pure function.
    • A pure function's return value depends solely on the values of their arguments.
    • A pure function can not modify the values passed to them.
    • A pure function is absolutely predictable with no observable side effects.
//Pure Function:
function square(x){  
  return x * x;
}

//Impure Function
function square(x){  
  updateXInDatabase(x);
  return x * x;
}
  • The reducer will take in two values:
    • The previous state of the app.
    • The action being dispatched.
  • Basic JS testing can be done using Jest (formerly expect).
  • You can use Default Function Parameters in the reducer to handle when the state hasn't been set yet.
  • As the reducer is always a pure function you can always use an ES6 arrow function.
  • The subscribe() method doesn't run until the state has been updated. This means it won't have access to the initial state.
  • Use the slice() and ...spread array methods to avoid Array Mutations in the reducer function.
  • You can test for mutations with the deep-freeze package.
  • The combineReducers() function generates the top level reducer for you.
  • You can explicitly pass the store fields down to the component props like this: <component propOne={store.getState().propOne} propTwo={store.getState().propTwo} /> or you can spread over ALL of the store fields inside the store like this: <component {...store.getState()} />
  • Action Creators can replace inline dispatches to the store. This can help with clarity and documentation of the actions your app can dispatch to the store.

Learn Redux

Learn Redux - Course by Wes Bos

Wes Bos has a FREE Learn Redux course that walks you through how to create a Redux app. It includes the basic concepts that was covered in the Introduction to Redux course with a little more explanation on how it plays with React.

A helpful nugget that I found was an explanation on how to expose your Redux Store to the Redux Devtools using the compose.

To do this we added the following to the Redux store store.js:

import { createStore, compose } from 'redux'

...

const enhanancers = compose(  
  window.devToolsExtension ? window.devToolsExtension() : f => f,
)

const store = createStore(rootReducer, defaultState, enhanancers)  

We now have access to the store in the Redux Devtools. This allow us to debug and timetravel through the application one action at a time.

Redux Store

Getting to Know our Redux Devtools

There are four main actions that you can take in the Redux Devtools:

  • Reset: Will remove all commits and revert the store back to your initial state.
  • Revert: Will revert all of the actions since your last commit.
  • Sweep: Will remove the selected actions and it will be as though these actions were never dispatched.
  • Commit: Will remove all of the actions from the log and will make the current state the default state.

Redux Devtools Quick Review

Further Explorations

Wes wraps up the course by inviting you to explore Redux further. For instance, we accessed all of our data from a file rather than an external API. This was for the brevity of the course and the ease of learning Redux.

You cannot put asynchronous calls within your reducers because they need to be pure functions that return instantly.

To work around this you can use Redux-Saga or Redux Thunk (we use the Redux Thunk middleware in some of our projects).

Wes also points to normalizr as a further addition for Redux. It is a small utility that normalises deeply nested JSON data.

There is also a giant repo of useful resources available in Awesome Redux.