Flux for components

The Flux pattern, without the globals

Flux is a software pattern, not a concrete artifact like a library or framework. This has encouraged developers to create various implementations of the pattern. I’ve explained an approach to the Flux pattern often enough that I felt it was time to put it into a blog. The approach creates instances for the Flux actors instead of using singletons.

Context

I’ve worked on Knockout based applications for years; since before frameworks like Angular and Ember were mature. Knockout though has been showing some of it’s limitations and age.

Knockout needs the data that it binds to the DOM to be wrapped in Knockout observables. Knockout observables end up spreading everywhere. If an observable depends on another observable in a different model class you will need to couple those two model classes by passing the observable. Classes can reach inside each other and access observables without a second thought. They would modify the observable value if they needed, there was no explicit ownership of model data.

For small to medium complexity components this is not an issue, you can hold the structure in your head. As components scale in complexity though, it becomes harder to correctly reason about your code structure and data flow.

Flux provides something that Knockout sorely lacks, structure and abstractions for scaling and a well defined and consistent data flow.

Constraints

Our applications are real time trading solutions. They must handle spikes in streaming traffic and high levels of updates. Clients could have 10+ FX tiles open in a layout, each receiving updates for a highly active currency pair. 4 updates a second per currency pair was a realistic benchmark. Tiles could be opened up, displaying multiple prices (different settlement dates or traded amounts would have different prices). This meant a tile could have 9 separate prices streaming in, one for each row in a ladder.

example FX application

We also have tabbed layouts which allowed clients to have background tabs open with a different set of tiles. These tiles would not be visible and we didn’t want hidden components to interfere with the performance of the application. In essence we wanted each tile component to be separate from the other tile components. This would allow granular control over every tile’s data subscriptions and behaviour.

Concerns with the Facebook Flux pattern

These constraints meant that we were wary of adopting the Flux pattern as implemented in the example Flux applications. The general data flow and abstractions, were appealing but the exact mechanics we wanted to tweak.

The change we made to the pattern was to make the actors of the system class instances. In the example Flux applications the stores, action creators and dispatchers are all singletons. This is fine if you only have one instance of a component (let’s say a shopping cart). With one component instance you don’t need multiple instances of its action creators or stores.

Things become more complex if you have multiple instances of a component all sharing the same action creators and stores.

Distinguishing data for component instances

With singletons each instance of your component must generate a unique ID and pass that around as a namespace for its data and actions. As all view components keep their data in the same store they need to be able to segregate their data from other view components. You wouldn’t want a chat message inputted in one chat box being added to every chat window. This results in some boilerplate code; code that passes around IDs and filters data based on these IDs.

getAllForThread: function(threadID) {
	var threadMessages = [];
	for (var id in _messages) {
		if (_messages[id].threadID === threadID) {
			threadMessages.push(_messages[id]);
		}
	}
	...
}

The snippet above, from the Flux chat example application, displays the sort of boilerplate required. The namespace here is provided by threadID. Not complex, but removing this accidental complexity is a plus. Given that our components have a high complexity and significant amounts of state we would need to add ID boilerplate code in a lot of classes.

Performance

Performance issues could arise due to the high volume of data events flowing into stores. The stores emit change events which trigger a view rerender. As all instances of a view would be registered to the same store they would all be notified of a store state change.

case ActionTypes.CLICK_THREAD:
	ChatAppDispatcher.waitFor([ThreadStore.dispatchToken]);
	_markAllInThreadRead(ThreadStore.getCurrentID());
	MessageStore.emitChange();
	break;

The snippet above, again from the Flux chat example application, shows how when the store changes its state it notifies the component views listening to it. If you have 10 chat windows and one action triggers an update to its store all 10 chat window views could trigger a rerender. There are several reasons why when using React it shouldn’t result in too much waste. Firstly it batches Virtual DOM diffing secondly the Virtual DOM prevents unnecessary DOM updates and finally you could implement custom shouldComponentUpdate methods for your components.

Given the high update rates for certain tiles, which could trigger rerender requests for tiles that might not even be visible we had to consider the possibility of needing those custom shouldComponentUpdate methods.

While the above concerns are tractable, using instances instead of singletons allowed us not to worry about them.

Implementing the changes

Using instances instead of singletons requires a different approach to accessing the actors in a Flux system. With singletons all you do is require the modules.

var MessageStore = require('../stores/MessageStore');
...
var ThreadStore = require('../stores/ThreadStore');
var UnreadThreadStore = require('../stores/UnreadThreadStore');

The approach we took was to use a factory. The factory API has getters for the actors in the Flux pattern.

class FluxDependenciesFactory {
	constructor(dispatcher) {}

	getDispatcher {}
	getStore(storeName) {}
	getUtility(utilityName) {}
	getActionCreator(actionCreatorName) {}

	registerStore(storeName, storeClass) {}
	registerUtility(utilityName, utilityClass) {}
	registerActionCreator(actionCreatorName, actionCreatorClass) {}
}

For each instance of a component, such as an FX tile, we would create a new factory and pass that factory into the root component as a view prop. The React Tile view component would pass the factory on downward to its child view components. From that point in the view downward the dispatcher, views, stores and action creators would all share the same dependency instances. These instances would be separate from the instances in another Tile view component.

const dispatcher = new Flux.Dispatcher();
const fluxDependenciesFactory = new Factory(dispatcher);

populateFactory(fluxDependenciesFactory);

React.render(
	<Tile factory={fluxDependenciesFactory} />,
	this._mountNode
);

The populateFactory function would register the constructors of the actors by their name.

function populateFactory(factory) {
	...
	factory.registerStore('TenorLadderRowDate', TenorLadderRowDateStore);
	factory.registerStore('TenorLadderRowButton', TenorLadderRowButtonStore);
}

The first time a request for an actor is received by the factory it will create it; any subsequent requests for that actor will return the same instance.

const store = this.props.factory.getStore('TenorLadderRowButton');

Drawbacks

The tradeoffs to this approach are having to pass the factory into your view components and the code to register the actors the factory creates.

Extra advantages

There are other benefits with this approach, apart from the ones mentioned above.

Right tool for the job

There is clearly an overhead to building components in this fashion. So it’s important to point out that this isn’t meant to be the one true way to build all components.

The chosen approach should be as simple as required and no simpler, sometimes it turns out that singletons are just too simple.