Flux vs. Redux: A Comparison

Dakota Lillie
8 min readDec 27, 2017

--

I’ve been delving into Redux for the past week or so, and I’ve grown to really appreciate how much it can simplify state management (once you get past the learning curve, at least). No more passing props through layers and layers of components. Instead, components can just tap into the store directly and grab what they need, where it’s needed. It’s a welcome simplification that I can imagine will facilitate scaling applications much more easily down the line.

However, one thing that hasn’t been entirely clear to me until now is how Redux relates to Flux. I knew from reading the documentation that Redux takes inspiration from Flux, though it isn’t exactly the same. But how exactly are they different? With that question in mind, let’s start at the beginning: why Flux (Redux’s precursor) was created.

The Need For Flux

It was 2011 and the engineers at Facebook had a problem: users would see that they had a chat notification, but when they clicked on it there would be no new message waiting for them. The team was getting consistent complaints about this from their users, and they worked hard trying to fix it. But each solution would only address some particular edge case, and no matter how much they did, the issue kept coming back. This bug came to be known as the zombie notification.

The problem was that the entire system was inherently fragile. Facebook was using the MVC architecture at the time, which was becoming increasingly unstable as the application grew more and more complicated.

The number of models and views had compounded as Facebook added new features, and the relationships between them had become unmanageably difficult to keep track of. Likewise, the code which dictated their interactions was largely imperative and easily prone to breaking. Something fundamental about the system needed to change, and it was out of these struggles that Flux was conceived.

Unveiled in 2014, Flux was an entirely new approach to state management. It’s goal was to make changes in state predictable (and thus more easily manageable, especially in complex applications) by imposing certain restrictions on how and when changes could happen.

What does that mean, exactly? Well let’s dive right in…

How Flux Works

One of the integral concepts of Flux is that all data must flow in only one direction. This unidirectional data flow is a significant part of what keeps everything so predictable.

To illustrate what this is (and why it is important), we can take another look at the messy MVC diagram above. There, the views and models have arrows running in both directions—in other words, the data flow is bidirectional. Bidirectional data flow becomes harder to maintain as complexity grows, because it gets increasingly difficult to pinpoint exactly where a particular change originates. Since the models and views are so complexly interconnected, effects can cascade in unanticipated ways, increasing the likelihood of unwanted bugs.

The unidirectional data flow enforced by flux helps us avoid these cascading effects. It looks a little something like this:

Let’s go through the pieces of this chart one by one:

Actions

Actions are representations of the ways users can interact with the application. They are Javascript objects, whose only requirement is that they must contain a mandatory type field, although they’ll also usually contain some data as well. They look a little something like this:

{
type: 'INCREMENT_COUNTER',
amount: '5',
}

The type field is a description of what kind of change will be made to the application’s state, and we’ll revisit it a little further on. Actions are created either upon initialization or through some interaction with the view, such as by clicking a button or submitting a form. They are then sent to the dispatcher.

Dispatcher

The dispatcher receives the action and forwards it to each of the application’s stores. A few important things to note here:

  • Any Flux application only ever has one dispatcher.
  • Every action is sent to every store.

In other words, you could say that the dispatcher exists to direct traffic, acting as a central interface between actions and stores. That’s all there is to it!

Stores

Stores are the structures which hold the application’s state, as well as the logic around how to update that state. As such, they are Javascript objects (usually defined as classes which are subsequently instantiated). An application can have multiple stores, each of which is responsible for a different portion of the application’s state. Stores register with the dispatcher upon creation, and operate according to a couple core rules:

  • State must only be changed (or mutated) in response to a received action. As such, stores must not have setters, only getters.
  • Each time a store’s data changes, it must emit a change event, which will be broadcast to the application’s views and instruct them to re-render accordingly.

I realize this is all highly conceptual and perhaps difficult to fully grasp without seeing some code. However, I hesitate to give a more specific example of what a store looks like because implementations of stores can vary significantly. Since Flux is a general methodology, not a specific framework, any implementation is going to be dependent on external libraries whose functionality is not immediately evident. As such, any given example would probably serve to obfuscate more than clarify.

What’s important to understand is that stores are objects which contain both a piece of the application’s state and the means of altering that state in response to received actions. Since all stores receive all actions, a store will determine whether or not to alter its state upon receiving an action by examining that action’s type. If the store has logic related to that type, it’ll take the appropriate actions, and then send a change event to any views that have been configured to listen for changes from that store.

Which brings us to the final piece of the puzzle:

Views

Views are what the user sees and interacts with. They are the interface for displaying the data from the stores, as well as for sending actions back to the stores through the dispatcher. Since Flux was designed alongside React, the two pair together nicely. However, Flux can work with any front end framework.

Now that we’ve covered all the pieces, let’s go through the full process. Say a user clicks a button within the view, marked “Increment Counter”. This sends an action with type: 'INCREMENT_COUNTER' to the dispatcher. The dispatcher receives this action, and pipes it along to each of the application’s stores (of which there may be several). The stores check their internal logic to see if they respond toINCREMENT_COUNTER actions. If they do, they make some change to their internal state, and then emit a change event, which will cause the appropriate views to subsequently re-render, presumably displaying some updated value for the counter.

Introducing Redux

I mentioned earlier that Flux is a generalized pattern, not a specific library. As such, there are a number of different modules and frameworks that can be used to implement it. But one framework in particular has distinguished itself as the most popular. That framework is Redux.

Co-authored by Dan Abramov and Andrew Clark in 2015, Redux aims to simplify and streamline many of the concepts introduced by Flux. Whether or not Redux is, strictly speaking, a Flux implementation is a matter of some debate. Redux and Flux are similar in that they both emphasize the importance of unidirectional data flow, and they both adjust state through actions with type fields. However, they have some differences which are important to note:

No Dispatcher

Redux does away with the concept of a dispatcher. It can do this because Redux only has a single store (more on that in a moment) so there is only a single destination to broadcast new actions to, thus eliminating the need for a dispatcher.

A Single, Less Convoluted Store

With Redux, all of your application’s state is located within a centralized store which acts as the application’s single source of truth. Additionally, the store’s responsibilities have been reduced — it is now only responsible for containing the state, and is no longer in charge of determining how to adjust its state in response to actions. The logic for that has now been delegated to…

Reducers

Reducers are pure functions which accept the current state and a given action as arguments, and which output either the unmodified state or a new, edited copy of the state. The word copy is important here — Redux considers state to be immutable. If the state needs to be changed, it is not edited directly. Rather, a copy of the state is made, and the reducer edits that copy, then returns it and replaces the original state with its modified copy.

Here’s an example of a reducer in code:

const counter = (state = { counter: 0 }, action) => {
switch (action.type) {
case 'INCREMENT_COUNTER':
return {
...state,
counter: state.counter + 1
}
default:
return state
}
}

When a Redux store is instantiated, it is given an argument of a single, root reducer, which is in charge of all the logic for that store. However, the reducer can have any number of sub-reducers which govern different sub-sections of state. This process is known as reducer composition. It’s a fascinating subject which I won’t get into too deeply here, but which is integral to the scalability of an application.

Redux Recap

That pretty much covers the core differences between Flux and Redux. Here’s an illustration of Redux’s data flow:

I realize much of this has been highly conceptual, so let’s once again run through a more concrete example.

As before, a user clicks a button on the view which is labeled “Increment Counter”. An action with the type: 'INCREMENT_COUNTER' property is sent to the store via the store’s built-in dispatch() method (in practice, an individual view component would be connected to the store through something like the React-Redux library’s connect() method, which adds an additional layer of abstraction to simplify the process). The store receives the action sent by the view and passes it to the reducer the store was initialized with, along with the store’s current state. The reducer (and any sub-reducers) check the action’s type to determine if any changes to state need to be made. If they do, the current state is copied, and the copy is modified and then returned by the reducer function, replacing the original. The store then notifies any views/components which are subscribed to the store (in other words configured to listen for state changes, again oftentimes through the connect() method), and if there are changes, the view will re-render.

Conclusion

Hopefully reading this has given you a better understanding of the differences between Flux and Redux. If you’d like to learn more, I’d recommend checking out the official documentation for both. They both come with examples and step-by-step walkthroughs, and Redux’s documentation in particular is fantastic. Redux also has a series of free video tutorials narrated by Dan Abramov (one of the co-authors) himself, which are very helpful.

--

--