Build Your Own Flux

Architecture in the Client

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.

What’s the idea behind our own Flux?

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.

{
type: ‘ADD_ITEM_TO_TOP’,
item: {
name: 'John Doe',
id: 1234
}
}
  • 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

Enter with a Service Handler

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
}
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
});
});
)}
);

Modify the State

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

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

Create a State Aware 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.

class StoryList extends React.Component { …}
class StoryList extends StoreComponent { … }
constructor() {
super();
registerFor({
startpageMessage: 'No information available',
startpageStories: []
};
)}
render() {
return(
<main>
<div class="message">
{this.state.startpageMessage}
</div>
<ul class="stories">
{this.state.startpageStories.map(story => (
<StartpageStory story={story}/> // #A
))}
<ul>
</main>
)
}

Implementing the StoreComponent

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)
}
}

Summary

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

  • the Dispatcher
  • the StoreComponent
  • 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()

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store