Demystifying redux for flux people

Redux is a flux implementation. Or at least, borrows heavily from the “flux mindset”. But as someone who came directly from flux implementation that closely mirrored the vanilla flux implementation facebook had, redux took some trial and error to sink in.

So, I’d like to draw a contrast between redux and vanilla flux here. For the ease of reference, I’d like to call traditional flux as just “flux”. Flux and redux are not two mutually exclusive ways of doing things though.

Out with the stores, in with the single object tree

In traditional flux implementations, you maintain multiple stores, which are also called “Fat models” to store your data. They will contain your application state. For different entities in your app, you will have different stores. So, you’ll be ending up with a UserStore, ApplicationStore, OrderStore and etc.

In redux however, the application state is maintained in one “tree”. Or, a single store. And your store would look something like:

And your store is immutable. Which means:

that you should not mutate the state object, but return a new object if the state changes

But how you maintain the store is totally up to you. I prefer to use Immutable.js for it’s immutable data structure and ops support out of the box. But you can use plain javascript objects, as long as you do aObject.assign({}, …) or something like that to return a new object without mutating the existing object.

Reducers change the state of your store.

A reducer is nothing more than a pure function. A pure function is something that returns an output, which is only determined by it’s inputs. Another way to describe it is to say that the function doesn’t have side effects.

And redux reducers have the following signature:

(currentState, action) => nextState

This just means that you should write a function that will take the current state of your application, and the action fired, and return the next state of your application. For an example:

We’re writing the userReducer here. which means this reducer will affect the user key of the object tree. When an action happens like LOGGED_IN or LOGGED_OUT, we will return a new object with the corresponding changes. If no relevant action takes place, default case will return the existing state without change.

Redux stores

A redux “store” is one or more reducers. There are no event emitters, and no explicit event emitting. We know a change happened when a reducer tells us that currentState !== nextState. And since state is immutable, we don’t have to do expensive change tracking.

Creating a store is as easy as:

let store = createStore(user);
                     // ^^^^ is the reducer we wrote above

Listening to the stores

To subscribe to changes in our stores, we can just, store.subscribe(callback). callback will fire when the store changes, and we can get the updated state of the store by doing a store.getState().

But more often than not, we have to bind our react components with the store. In those cases, weconnect the relevant redux store with the component’s props. Listening to the entire tree is not good. So, we select what’s injected to this.props as well.

Our selector would take the state and return a subset of it, that’ our component is interested in:

connect helper is available in react-redux package.

import { connect } from 'react-redux'

Given our component LoginForm, we will export is as:

export default connect(select)(LoginForm)

Now, Login form will have this.props.user and this.props.appConfig from the store. Note, that we don’t have to manage event listeners or subscribe to stores within the component lifecycle. The component will recieve new props and will be re-rendered.

Dispatching Actions

We discussed that store has a getState and a subscribe in it’s API. The third and final element isdispatch, which will let us dispatch actions.

So, we can just as easily do,

store.dispatch({ type: 'LOGGED_IN', payload: {...}});

And that will hit our reducers, thus completing the cycle of unidirectional data flow.

You should know that if you connect your component, you will recieve dispatch asthis.props.dispatch as well.

In your app, you will usually create “action creators”, that will create actions that will fire. Like this:

And from your components, you will simply do a store.dispatch(logoutSuccess()). But to do aloginSuccess, first you will need to call a service, authenticate user, and get the userData back. But doing that would probably be an async action.

Firing actions asyncronously

The easiest way to get that done is through a middleware called redux-thunk. You will have to connect the thunk middleware with your store with redux.applyMiddleware as explained here.

This is the difference between dispatching an async action vs. a sync action:

Note the incrementAsync returning a function (dispatch => op) rather than an object as inincrement. Our login example would look like:

Closing notes

Compared with other flux implementations, redux has a much simpler mental model, and less boilerplate as well. The excellent documentation at redux.js.org covers everything in-depth. Giving that a read will most certainly help. Redux enjoys the benefits of immutability and does away with the change tracking and event emitters — and that makes it a bit of a “jump” from traditional flux.

Other than that, you might want to look into:

  • redux-actions will help you further reduce your boilerplate and help you with creating actions that conform to FSA. (Flux standard actions).
  • reselect will help you improve the performance of your selectors, and create composable selectors as well.
  • redux-logger will log your actions beautifully by plugging into your store as a middleware. Super helpful in debugging.
  • redux-devtools read “A live-editing time travel environment for Redux”. Helps you replay actions and persist state across multiple pages.
Written on March 28, 2016