Functional
Reactive React.js

After watching a compelling talk about FRP by Jafar Husain from the Netflix UI team, I’ve been fascinated by the way reactive programming can make code both effective and elegant.

At Football Radar, we work with many different streams and sources to create rich interfaces that capture realtime data about football matches, and we’re convinced that our products are a perfect fit for reactive programming.

In this post, I will discuss the way we approach building interfaces with reactive programming and React, and how it’s changed the way we think about writing effective code. The first half will cover how we use reactive programming — RxJS, more specifically — to compose our data sources, and the second will talk about feeding that data into React.

Composing streams

I use RxJS as my FRP library of choice, but there are some excellent alternatives, such as the popular Bacon.js and the more recent arrival in Kefir.js. The examples below can easily be adapted to use any of these libraries.

Many of our data sources arrive over WebSocket, providing a simple event-driven interface for consuming messages. The first step is to translate this into an observable, which we can do in the following way:

var observeGameEvents = function() {
return Rx.Observable.create(obs => {
var fn = ws.subscribe(“games.*”, obs.onNext.bind(obs));
return function() {
ws.unsubscribe(“games.*”, fn);
};
});
};

This creates a read-only observable for a series of realtime game events. Subscribing to this observable will give us a stream of data that looks something like this:

{ gameId: 1, key: “goals”, value: [2, 0] }
{ gameId: 2, key: “clock”, value: 182 }
{ gameId: 1, key: “clock”, value: 481 }

Each item captures some changing value about a game. With this observable in place, we can now compose our data model via some simple functional transformations.

Capturing the data model

Our observable gives us a sequence of events, but what we might actually need is to capture the aggregated state. In functional programming terms, we need to perform some kind of map-reduce over the data to transform it into a shape that we can pass around our application.

If these events were in some kind of iterable, we could simply write:

gameEvents.reduce((gameState, event) => {
return update(gameState, {
[event.gameId]: {
[event.key]: { $set: event.value }
}
});
}, {});

For brevity, I have borrowed React’s update immutability helper for this example. As an aside, using immutable data in functional programming is encouraged, as it allows your functions to remain free of side-effects.

Our code is almost identical, but there is a small caveat: unlike an iterable, which has a known length, an observable is often open-ended, so we have to use the slightly less familiar scan operation. This is basically an incremental reduce, performing an aggregation step for each item in the observable sequence as soon as it arrives.

var observeGames = function(gameEvents) {
return gameEvents.scan((gameState, event) => {
return update(gameState, {
[event.gameId]: {
[event.key]: { $set: event.value }
}
});
}, {});
};

This function takes the game event observable that we created above, and builds a complete game state for each observed game.

This is not the only way that we could interact with our stream of game events, however. Perhaps we would prefer to build ticker of live scores. By observing only game events about goals, we can easily implement this:

var observeGameTicker = function(gameEvents) {
return gameEvents
.filter(event => event.key === “goals”)
.map(event => {
return {
id: event.gameId,
goals: event.value
};
});
};

This function takes our original game events observable and returns a new observable pushing goal events for all the games in our stream.

This really only scratches the surface of what is possible with observables — the API that RxJS exposes, for example, is extensive and certainly worth exploring.

Rendering data

With our data streams in place, rendering data in a reactive way becomes effortless. For the game ticker, our React component might look a little something like this:

var GameTicker = React.createClass({
renderGames() {
return this.props.games.map(game => {
var score = game.goals.join(“ — “);
return (
<li key={game.id}>
{game.teams.join(score)}
</li>
);
});
},

render() {
return <ul>{this.renderGames()}</ul>;
}
});

This component takes a list of games with a list of teams and goals, and outputs the current scores for those games. By combining this component with the observable we created above, we can easily create a realtime game ticker.

var gameEvents = observeGameEvents();
var gameTicker = observeGameTicker(gameEvents)
.map(mergeTeams)
.windowWithCount(5);
gameTicker.subscribe(games => {
React.render(
<GameTicker games={games} />,
document.querySelector(“#ticker”)
);
});

This code is concise, but that belies its expressiveness. First we create an observable for the stream of game events.

var gameEvents = observeGameEvents();

Next, that observable is used to compose a second observable, this time for the game ticker events.

var gameTicker = observeGameTicker(gameEvents)

We then map over each item, merging in the teams for that game (although not implemented, assume mergeTeams has access to some mapping of game IDs to teams).

.map(mergeTeams)

Next, we perform an incremental reduce over this observable — much like scan in a previous example — and return a moving window of the 5 most recent events. This window is a fixed-size rolling buffer that updates each time a new event is received.

.windowWithCount(5);

Finally, we subscribe to the observable, which starts the data stream and captures a list of 5 games. These games are passed into our GameTicker component and React renders into the DOM.

gameTicker.subscribe(games => {
React.render(
<GameTicker games={games} />,
document.querySelector(“#ticker”)
);
});

Each new item pushed through our observable triggers a render pass, making this a completely reactive model for working with React. Our component knows nothing about the data source, but is still updated on incremental data changes.

Closing thoughts

I believe that FRP is an excellent corollary to React and the Flux architecture, as both encourage composition and unilateral data flow.

There are always likely to be trade-offs in software development, but this combination comes closer to a silver bullet than anything I have worked with before.

We have some exciting projects on the horizon at Football Radar, such as integrating video streams into our UIs and working with motion tracking, so it will be interesting to see how well a reactive approach fits with these varied types of data.

Further reading/viewing

Async JavaScript with Reactive Extensions (video)
This excellent talk by Jafar Husain at Netflix was the inspiration for this post and for my early forays into functional reactive programming.

Reactive UIs with React and Bacon
Although I prefer to implement React components in a more stateless way — the author favours state over props — this gives a great introduction into working with React and Bacon.js.

Show your support

Clapping shows how much you appreciated Gary Chambers’s story.