Facebook’s React and Flux were the most interesting developments of 2014 for angular developers. Not that angular developers should all go jump ship and hop on the React train. But rather, that React and Flux can teach us ways to architect better angular applications. By taking cues from React and Flux, we can create applications that are simpler, more scalable, faster, and easier to test.
Update: This article is now part of a series about Flux and Angular:
PART 1. How can React and Flux Help us create better Angular Applications?
PART 2. Achieving Reasonable and Scalable Routing in Angular with Flux
For the purpose of this article, I’ve implemented in two different ways a simple example app named color experiment. The first implementation uses standard angular constructs to build a widget that allows you to filter a grid of colors with a directive called ColorSelector. The other implementation uses Flux with angular to build the same widget. Before you continue with this article, please mess around with the color experiment demo for a moment.
React & Flux Concepts
If you’ve read about Flux already, then you probably know that Flux is an application architecture which doesn’t specifically rely on React. In this sense, you can apply Flux to pretty much any framework or library. However, I think that it’s important to recognize that Flux is the result of a natural progression of concepts that emerged out of React.
React Concept #1: Stateless Components
React components should be as stateless as possible. This leads to the creation of rather dumb components which generally receive all of their state data via DOM attributes, and do not directly mutate that data. Similarly, in angular we may implement stateless directives which also receive state data that is passed in with attributes, and do not directly mutate that data. For example, consider the following directive implemented in two different ways. Note that the stateless directive does not mutate scope.colors nor does it manage scope.selectedColors, while the stateful directive mutates both of these objects in response to user interaction.
Flux Concept #1: Unidirectional Data Flow
Unidirectional data flow is, in part, an evolution of the stateless components concept. React was designed with the ability for child components to communicate back with their parents. In angular, we can accomplish child-to-parent communication with two-way data binding, or event emitting.
However, Facebook engineers recognized that allowing data to flow in two directions created applications that were difficult to reason about. Hence, the concept of unidirectional data flow was introduced. Specifically, Flux says that data should flow down: starting at the topmost component and then down the tree.
So what happens when a child needs to communicate state to a parent component? For this purpose, Flux introduced the dispatcher, which is the only part of Flux that is actually a concretely-defined thing. Below is Facebook’s standard diagram to explain Flux.
To facilitate the unidirectional flow of data, they also introduced Action Creators and Stores. If you’re new to Flux, it’s important to realize that Action Creators and Stores aren’t really concrete classes nor implementations. Rather, they are prescriptions about how certain types of code should be grouped. Asynchronous code goes in the Action Creators. Logic related to mutating the state of an application goes in the Stores.
In color experiment flux, here’s what the unidirectional dataflow looks like:
React&Flux Concept #2: Single Source of Truth
What we can deduce from the diagram above is that the truth about the visual state of the application is managed by the store. The store is the only piece of the application flow that is tasked with mutating state data. In this case, the store alters the selected property of the color object. It also calls this.updateSelected() which further mutates data which is managed by the store.
Single Source of Truth also means that we don’t keep copies of state data in various parts of our application that we have to then worry about keeping synchronized. For example, there is just one instance of the colors object stored within the store.
React Concept #3: DOM Diffing
Suffice to say, that in many cases React is much faster than angular. Develop with angular enough, and you will run into situations where the dirty-checking digest cycle is too slow for the requirements of your application. For these cases, a rather painless solution is to use ngReact. ngReact allows you to include React components inside of an angular application. The first React component I ever built was a re-write of a 20-month calendar widget originally written in angular. The rewrite took about a day which is a testament to the rather easy learning curve of React and the simplicity of ngReact. I didn’t even attempt to do any shouldComponentUpdate() optimizations in the React component and it was several times faster than it’s angular counterpart.
The role of ng-model
In color experiment, I used the ng-model directive to create toggle buttons out of checkbox input elements.
However, in color experiment flux, I swapped out the checkboxes for simple divs and because of the unidirectional flow it was unnecessary to use ng-model.
The resulting code is much simpler and easier to manage than it’s two-way-binded alternative. It also doesn’t have to live in the directive’s link function. Instead, I moved it to the store, where all state logic lives in a Flux application:
So should we throw away ng-model entirely? Probably not. I suspect that ng-model will still be handy in use cases which call for validation, but this is something I have yet to really explore in an angular application that utilizes the Flux architecture. My theory is that in order to prevent a directive utilizing ng-model from becoming stateful, we should clone any state data going into and coming out of ng-model, and this would essentially preserve the advantages of using flux (update: check out this response by Wolfram Sokollek, he solves this very nicely). This is similar to the approach used by flux-angular, a flux framework for angular which forces cloning of all data going into and coming out of stores.
The role of ngRoute & ui-router
After using these two guys for a while (mostly ui-router), I have come to the conclusion that the two most popular routers for angular are mediocre to poor at handling application flow in complex asynchronous scenarios.
I have an angular flux-based solution which minimally uses ngRoute along with standard flux concepts to handle application flow and routing at the same time. However the use case it tackles is not terribly complex, so this is another area that I’d like to spend more time exploring.
Update: I’ve written another article that robustly solves Angular+Flux routing.
Where does stuff go? Solved.
One of my favorite things about Flux is that it goes a long way to solve the question that’s been plaguing angular developers, where do I put stuff?
- Asynchronous stuff? In the Action Creator.
- Stateful mutation stuff? In the Store.
- Message Passing stuff? Dispatch an action.
Speaking of message passing, what about angular events? With flux, you can still use them, but in a much more restricted domain. Specifically, if you want to broadcast an event from a store after it receives an action and mutates data, you should use the angular event system. For most other messaging situations, the flux dispatcher is probably the way to go.
Visualizing Application Flow
… and here’s what we get with a more complex (but contrived) application:
Notice that in every application, there is a very predictable pattern:
View (white) → Action Creator (orange) → Action (yellow) →Store (blue)
- color experiment: angular, non-flux plunkr for this article
- color experiment flux: angular+flux plunkr for this article
- Achieving Reasonable and Scalable Routing in Angular with Flux: Part 2 of this article.
- simflux: My simple wrapper for Facebook’s Flux Dispatcher.
- vizone: My application visualization Chrome plugin that works exceptionally well with simflux. (Built with React, simflux, Morearty, and zone.js)
- Why you should use ngReact instead of just plopping a React component into your controller.
- flux-angular: Takes a substantially different approach to using flux with angular than the one presented in this article.
- Integrating React and Flux with Angular
- Functional Programming Should be Your #1 Priority for 2015