A Primer on Redux for Data Visualization Professionals

This is part two in a series. If you haven't already read React.js for data visualization, part 1, consider starting there.

In the last section I sang the praises of React.js for use in data visualization. Before we build some examples, I wanted to introduce just enough Redux for you to be dangerous.

Redux is a library for managing state. Doing things the "Redux way" means aggregating all your state into a few immutable objects (more on that in a second) and changing the state through Reducers that handle events which are generated by users and scripts. We'll get into why you'd want to do all these things a we go through the process.

Let's talk about immutability first. Immutable objects are objects that cannot be changed. They can be transformed into new objects, but the old record is untouchable. Think about editing a page on Wikipedia. Your change will alter the way the page appears to others, but the old state is still there, immutably, in the page history.

This has always been an important concept in programming language, but the tools and reasons to use immutable objects are growing. That's more of a topic of software design. For a data scientist or someone interested in data visualization, I'll skip some of the details. For me, the key takeaway is that you need to think of the state of your system like a snapshot in time. The state must contain every parameter necessary to describe your visualization.

In this way, the state space of your visualization is all the potential values of the parameters. That state space might be huge and not easily to write out, but you can think of any given snapshot as just one possible state. Actions change that state, but the idea of the previous state remains. In this way, creating things like Undo functionality can be very easy!

The first key concept is that of the Reducer. A Reducer takes the (immutable) state and a particular event. The Reducer must produce a new state which reflects whatever changes should happen given the event. Let's look at a short code sample.

There are many ways to write Reducers. In fact, I think purists might criticize my code as potentially being inefficient because i transform the state, rather than work on it directly. However, this code pattern has been incredibly performant for me and helped avoid errors the team I was managing encountered when we were trying other patterns.

The prior_state value is what the state of your system should be for a brand new user. In my case, the state is described entirely by the parameter value which begins at 0.

The reducer function is called whenever an action is dispatched (we'll talk about that later). The action has two parmaeters: type - a string that you get to set and payload, an arbitrary object that you choose to pass in. I find it most helpful to always pass in dictionaries with values. Thus, in the example above, you can imagine an action like this:

var action = {type: 'CHANGE_VALUE', payload: { delta: 2, magnitude: 1 } }

I use a case statement to hook onto the various actions. Typically there would be more than just one.

You will notice the first line of the reducer function (var nstate = state.toJS()). The state begins as an Immutable object. Immutable is a great Javascript library, but one you'd have to learn and might not benefit from as a data scientist, so I'm kind of cutting around it here. The .toJS() function converts it into a simple Javascript object that will feel a lot like Python. My input state is untouched, and I have a copy in the form of nstate which I can mutate. As the return statement, I simply re-Immutabltize (let' agree that's a word), and return that. This makes Redux happy and lets me easily manipulate the state in whatever manner I find appropriate given the action.

There's another great separation of concerns that you should be starting to see. As you develop your system, you can freely dispatch actions whenever you like. If the reducer doesn't know how to handle that action, it will do nothing. You might want to have a default line and print a message about it being missing to help with your development. As you manage user input and asynchronous actions, you can simply dispatch an appropriate action and worry about the handling logic later. I find this incredibly helpful to my workflow.

Dispatching an action is fairly easy to do programmatically, however, there are a few first steps that we'll get into in future parts of this blog series. For now, trust that inside your Component, you have a variable called dispatch passed in as a property. You can then write these two lines of code:

var dispatch = this.props.dispatch

dispatch({type: 'SET_FOCUS_BLOG', payload: {delta: 2, magnitude: 1} })

That last line's input should look very familiar to you from above.

It's that easy. You dispatch events and provide the Reducer that knows how to process them. Redux takes care of all the nitty gritty detail about getting things queued up and run. React.js takes care of re-rendering the system appropriately as the state changes.

In the next sections we'll start talking about how to get a sample project going.