Wiring the AJAX speaking backend with a Javascript Webview

React.js is kind of a revolution in the design of big single page targeting frontend frameworks. The contenders Angular.js and Ember.js are both opinionated in design and cover solutions for the whole stack of data flow in the browser. They include data management and even connection to your backend. The revolutionary concepts most experts talk about is the so called Shadow DOM, a lightweight copy of the DOM combined with a very intelligent algorithm to create least possible impact on the real DOM of the page.

But to me the real revolution is the lack of opinion, the concentration on just one task and doing that task very well. Some may say that’s a step back in the time where everyone scamped everything, because the javascript language is so open. I think on the other hand, that this data distribution and flow control is a rather small layer that needs a lot of tweak concerning your own infrastructure. It will work completely different in a modular multi purpose navigation heavy environment like xing.com compared to a workflow that is for example needed to consecutively display incoming images like on the react based instagram.com.

So Facebook did the right thing, I think. Provided us with an idea of a possible architecture and with a paradigm of one direction data flow — and called that system flux. Flux can be implemented in an overseeable amount of lines of code, probably not much more than taking and adapting an existing implementation like the promising Redux framework. In my opinion, the best and most easy to maintain solution is to build your Flux from scratch.

Well, to be honest, these few lines will be responsible for all the interactivity of your site — not only of a single page. So it will become somehow the most important code in the final application. If the resulting API is easy to use, it will be easy to write modules for it — easy testable, highly responsive and with clean interfaces for inter modular data exchange. If it’s difficult, you will get error prone application with long bug fixing lifecycles.

So what core concepts does Flux offer?

The main principle is the single direction data flow. One simple package of data (called action) can be created by any occasion throughout user or server interaction.

A typical action looks like this:

{
type: ‘ADD_ITEM_TO_TOP’,
item: {
name: 'John Doe',
id: 1234
}
}

This will be processed by a dispatcher (and exclusively by that), which manipulates the data state of the page (in one or more so called store objects). And finally this store is directly linked to the states that are part of React’s Components.

As soon as the user interacts with one of the components, for example clicks a button, nothing happens but the emit of such an action. As such, the action is similar to an event. For the dispatcher, the action more or less resembles a message like in an actor model architecture (e.g. Akka).

Several Implementations of that idea exist in the meantime and some of them as well as our own requirements have added some quite interesting ideas to it:

  • Other than the proposal in Flux, we would allow only one big store containing all the application state
  • The dispatcher itself is silly, but can be enriched by handler functions, that register for action ids
  • We divide these functions into two types:
  • reducers (borrowed from Redux) work in the context of the store and define the next state of it. These reducers are pure functions as defined in functional programming. They are processed without any side effects to the rest of the application, just transforming an input into an output - in that case a completely new object holding the next state.
  • services define the transition to all backing systems like workers, access to local storage and of course the ajax interface of the backend.
  • The store itself is modularized into one core part and one section for each registered module.
  • We subclass React.Component to connect React to this system. Once connected the Component gets its state exclusively from the store

The adapted workflow would look like this:

Let’s get started at the beginning with fetching some data from the backend. At first there is an action triggered, that tells the system that the user enters a module:

{
type: 'STARTPAGE:ENTER',
user: 12345
}

We now concentrate on fetching data for the startpage of the currently logged in user while other parts prepare for the transition, e.g. show a spinner … so there should be a service function for such a task.

dispatcher.registerService(
'STARTPAGE:ENTER',
function(action, dispatch) {
http.get('/user/' + action.user + '/stories').then(stories => {
dispatch({
type: 'STARTPAGE:INCOMING',
stories: stories
});
}).catch(err => {
dispatch({
type: 'STARTPAGE:SERVER_ERROR',
error: err
});
});
)}
);

As you see, services listen to action types and then get connected to the backend and do all the interaction with other parts of your application. But in the end, as soon as data arrives and changes need to be made to the UI, again only an action is being dispatched, which might look a bit strange and too much at first, but exactly this is responsible for the single direction data flow and the consistency of your application. As you also see, it is independent of time and may as well get handled asynchronously.

One major aspect in this case is to remain independent from the store, so the action must contain all the payload it needs to fulfill the task standalone. This aspect also makes this function easier testable although it’s not yet a pure function as known from functional programming.

Next up, you should begin updating your application’s state. After all, you already have three different activities on your system.

Let’s get started with some initial stuff. On entering the Startpage, we would already show the user, that something is happening - even before the stories are coming in.

We use a reducer for this. A reducer is other than the service function mentioned before a synchronous function, you can think of it as a function that could be used as a callback for javascript’s Array.prototype.reduce(). The signature of this callback is defined: reducer(stateTools, action). Instead of the existing state, you get an object called stateTools with some handy helper methods to manipulate the state safely. The function must always return the new state by using a function called stateTools.transform(). In order to have access to the previous state, you can use stateTools.get() to retrieve single keys from the state.

So let’s start implementing our first reducer:

dispatcher.registerReducer(
'STARTPAGE:ENTER',
function(stateTools, action) {
return stateTools.transform('startpage', {
message: 'Your latest stories are being fetched'
});
)}
);

As you see, we don’t do much heavy lifting here, just adding a message to the state, which we later on introduce to the UI. The transform() function is a helper, that creates new states out of the old ones and returns it. But why don’t we just alter the state? The state is immutable and therefore almost free of side effects. It can only and exclusively be altered by reducers triggered by actions and so you’ll always get exactly the same state when you provide the same actions.

Let’s create a slightly more complex example and add our stories. Please have in mind, that this will most likely happen at a different point in time, but that doesn’t matter.

dispatcher.registerReducer(
'STARTPAGE:INCOMING',
function(previousState, action, transform) {
return transform(previousState, {
startpageMessage: '',
startpageStories: action.stories
});
}
)

So now the loading message is gone and the stories are stored ready to display.

But nothing really happend up to now in your UI. If you’re already familiar with React.js, you know that the Components have still their initial state. So the next step will be the Component.

React Components already have a property called state that is directly linked with the rendered JSX Template. The magic is done by the Component.prototype.setState() method, which alters the existing state object and triggers the rendering process.

In order to avoid boilerplate code and rewire every component that should listen to the store, we use object inheritance and put a parent class between React.Component and our concrete Component implementation.

So instead of writing

class StoryList extends React.Component { …}

we create a Store aware Component with

class StoryList extends StoreComponent { … }

And to perfectly wire the component with our store, we only listen to some of the properties that are saved in the store. There is a convenience function in StoreComponent that accomplishes all that. Just call registerFor() in the constructor.

constructor() {
super();
registerFor({
startpageMessage: 'No information available',
startpageStories: []
};
)}

As you see, yo can provide all keys and even have some defaults in the case that the store has no entry yet.

In the end, you just use this to layout the Component in React’s render() method

render() {
return(
<main>
<div class="message">
{this.state.startpageMessage}
</div>
<ul class="stories">
{this.state.startpageStories.map(story => (
<StartpageStory story={story}/> // #A
))}
<ul>
</main>
)
}

One of the most important design goals is to reduce the number of store aware components to an absolute minimum and to use reacts option to hand over properties (asy you see in #A) to stateless, reusable child components. In the end these are the building bricks of our site as a whole.

There are only three parts needed to implement this whole flow, so it’s really easy to create and maintain it on your own. The most crucial part doing the most magic is the StoreComponent mentioned before. And this is how it looks like:

class StoreComponent extends React.Component { // #A
constructor() {
super();
this.store = dispatcher.getStore(); // #B
}

componentDidMount() {
this.listenerId =
this.store.onChange(
this._handleChange.bind(this)
); // #C
}

componentWillUnmount() {
this.store.removeListener(this.listenerId); // #D
}

registerFor(items) {
this.registeredKeys = items;
this._setStateFromStore(); // #E
}

_setStateFromStore() {
const newState = {};
for(let key of Object.keys(this.registeredKeys)) {
newState[key] = this.store.get(key) !== undefined ?
this.store.get(key) :
this.registeredKeys[key];
}
}
this.setState(newState); // #F
}

_handleChange(state, keys) {
if(this._intersection(keys, this.keys).length > 0) {
this._setStateFromStore(); // #C
}
}

_intersection(keys1, keys2) {
return keys1.filter(key => keys2.indexOf(key) > -1)
}
}

Let’s go through the steps:

#A: The new store aware Component inherits from React.Component, so the prototype chain still points to React

#B: Store is bound to the Component (just for convenience)

#C: componentDidMount() is the perfect place to start listening to store changes. Now every change is reflected to the Component. See also the _handleChange() function.

#D: componentWillUnmount() ends the connection to the store again - by removing the listener.

#E: here is the registerFor() function mentioned earlier. It just saves the requested items with their default values and calls _setStateFromStore()

#F: Now this is the implementation’s heart. We fetch all requested keys (from registerFor()) and use the defaults in case they are not yet set in the store. That’s important: we do not set the defaults in the store, because changes to the store should exclusively be done by the reducers. After all is fetched, we set the retrieved data as the new state and the component automatically updates it’s view.

So you see, that only a few lines are needed to build a concise, testable and first of all heavily usable Architecture.

I this article, I only showed you the Implementation of one of the three components which are

  • the Store
  • the Dispatcher
  • the StoreComponent

All of them consist of not more than 50 lines of code. So with a sum of 150 lines, you can create an easy to use Flux based framework.

Surely Ialready think of some extensions like

  • having module based partitions in the store
  • creating a garbage collector, that automatically deletes module data from the store if the user has left a module
  • connecting everything to the browser’s local storage to preserve settings for the user
  • creating a rewind/undo function, which should be easy, because our reducers are fully compatible with Javascript’s Array.prototype.reduce()

Finally, the framework needs to be filled with life by providing reusable components like form wrappers, a grid system, lightboxes and other fancy stuff.

Up next, I will work out a solution for lazy loading built into this framework, so only the absolute minimum of assets should be loaded (ideally in an HTTP/” friendly way). This will be the world of Proxy Components and Routers.