Christians 'G' logo

Choosing Redux

March 06, 2017

Choosing Redux to manage state in an AngularJS application

Redux is not as much a library as it is a programming pattern. And it is a powerful pattern. With an API of only 5 functions, a footprint of 2kB, and written in a some 200 lines of source code, Redux can enlighten the path to better state management. State management does not have to be a dreadful, unpredictable bug-magnet. It can be both fun and predictable.

Predictable state management

Redux encourages the entire state of the application to be a single source of truth persisted in a global store. This results in dump components that are truly dump. The only way for components to modify the state is to dispatch an action to the store. In turn, the components subscribe to state changes, and can update themselves upon getting these. In other words: state management is unidirectional and thus predictable.

Testable state transitions

Because each state transition is triggered by an action on the view layer, the real transition of state is handled in reducers hooked into the store. A reducer is a middleware, that each action passes through. If the action is meaningful to a particular reducer (i.e. it has the correct type), the reducer will manipulate the state using that action.

A reducer is a function just like one passed to the higher-order Array.prototype.reduce. It takes an accumulator (the state), a value (the action) and returns the new accumulated value (the new state):

reducer(state, action) => state

Redux requires reducers to be pure functions, meaning that any state transition can be easily provided mock data and unit tested.

The state is read-only

The most evident source of bugs in AngularJS applications is by far directives manipulating state [citation needed]. Directives become big classes that use internal state and tangled methods to manipulate some data that is two-way data bound with other directives. It is hard to reason about, it is hard to test and often the problem just keeps growing. A read-only state will eliminate the need for two-way data-binding and thus, as explained earlier, keep the views free from state transition logic.

More boilerplate, but worth it

Redux requires some boilerplate to enforce predictable, testable state management. And in some cases it would even require more code than if the state change was done in an ordinary “Angular way”.

The simplest example (unfair to Redux) is a panel that is toggled using a button. In an ordinary implementation, the flipping of a panelShown boolean could be done using a single expression inside an ngClick directive on the button. The panel would then have an ngShow directive responsible for showing and hiding it.

In a Redux implementation, these two directives would still be in place, but instead of flipping a boolean, the ngClick needs to dispatch a ‘TOGGLE_PANEL’ action that runs through a reducer. The controller would then need to subscribe to the new state and apply that to the view. That’s at least three new functions added to the code base.

A larger codebase is usually harder to read and reason about, generates more bugs and so on. But naive code generates bugs too. The three extra functions are simple, pure functions. The fact that the state transition is now unidirectional, testable and predictable is arguably worth it.

Enables super powers

When each state transition is described using a actions applied to the prior state, a lot of doors suddenly open. Ideas like adding “undo” functionality to an editor or saving the panel toggle state on browser refresh are not even far fetched - they are implementable. Saving state transitions in LocalStorage and having the ability to “replay” them when needed is just too cool to grasp.

This website is authored by Christian. His full name is Christian Hamburger Grøngaard (that's right - Hamburger.)

Christian at Aurlandsfjellet
This is Christian on Aurlandsfjellet

Currently he works as a front-end developer at Escenic AS where he fights complexity in large applications for newsrooms. He's been developing for the Web on and off since the early 2000s, got a good eye for design and UX and used to pursue a more design-oriented career.

When Christian isn't glued to a screen he enjoys singing and playing the guitar, hiking and skiing as well as spending time with his family in the wonderful city of Oslo, Norway.

Christian's online presence includes a Flickr profile, a GitHub profile, a Last.fm profile, a LinkedIn profile and a Twitter profile.

Copyright © 2019 Christian Hamburger Grøngaard{src}RSS

Other Writings

  1. Conversations in Code
  2. Git (and how we Commit)
  3. Immutable JavaScript
  4. Hashes and Salts
  5. Currying JavaScript 🍛
  6. Choosing Redux