Building an App with React and RefluxJS

A couple of months ago, I learned React by writing a game called Flip. Since then, React and the Flux architecture have attracted a lot of attention in the web developer community. Because of React's superior DOM performance, and the scalable nature of Flux’s one-directional data flow, many in the industry suspect that React+Flux may become the "next big thing" in front end web development.

Facebook doesn’t provide a framework for Flux, only how to make one from scratch. So I began searching for implementations. Surprisingly, there are already a few good (though somewhat poorly-named) options: jFlux, Fluxy, Fluxxor, and finally, Reflux. Of them, Reflux appeared to have the most backers and the most elegant API. I was also won over by Krawaller’s explanations of the overarching philosophy behind Reflux and how using Reflux can dramatically simplify a Flux application.

Reflux has a small and simple codebase of around 13kb minified. The API is clearly defined and often highly intuitive. It took me some time to acclimate to the “Flux way” of thinking, during which I found that old ways of thinking can get you into frustrating situations. Luckily, considering the age of Flux, there are a lot of resources available. Throughout the process of building my app, I found Krawaller’s posts, Facebook's overview of Flux, and the solid Reflux README to be indispensable references.

Over the past month, I’ve been hacking away at a Reddit-like demo app using React+RefluxJS+Firebase, and while it’s still a work in progress, I think it’s turning out to be quite a good looking project. Here’s what I learned.

What I Learned

One Store, One Component

Initially, I thought that I should create a Store for each type of data: userStore , postStore , and commentStore . This doesn’t work well in Reflux. Unlike other Flux implementations, Reflux publishers (Actions and Stores) can only ever emit one type of event. It’s possible to use Actions to persuade a Store to emit different data, but it’s unnecessarily difficult to orchestrate. It also makes stores more difficult to test. This is an intentional limitation of Reflux. By removing name strings from events, Reflux eliminates the need for a global dispatcher, drastically simplifying application data flow.

This problem is better solved by separating Stores by component rather than data type. In React News, I have a Store for each view ( singleStore , postsStore , and profileStore ), one for the login form ( loginStore ). I kept the userStore , but it is exclusively syced to my top level component and user data is passed down via props.

Since Stores are only supposed to emit one type of event, there are not many reasons for multiple components to listen to the same Store. But there are some exceptions. In addition to my top level component, my login component also needs to know when a login event fires so it can reset its form. The data doesn’t matter, only that the event fired. It can safely listen for an update from userStore and then reset its form.

var Login = React.createClass({ mixins: [Reflux.listenTo(userStore, 'resetForm')], resetForm: function() { this.setState({ submitted: false, }); this.refs.email.getDOMNode().value = ''; this.refs.password.getDOMNode().value = ''; this.refs.submit.getDOMNode().disabled = false; }, // etc... });

Single File For Actions

Similarly to Stores, I tried splitting my Actions into separate files: userActions , postActions , and commentActions . But I found that sometimes a postAction would need to call a commentAction , or vice versa, and circular dependencies started happening. So I moved all of my Actions into one file and that was that.

Actions calling Actions vs. Stores calling Actions vs. Stores calling Stores

In the real world, sometimes Actions need to be fired once an async request completes. There is a discussion to be had about whether it’s kosher to initiate an Action from a Store, say, right before it triggers, or if it’s better to move API requests to the Action’s .preEmit() method and call other Actions from there. I prefer the latter, as it better maintains one-directional data flow:

actions.addComment.preEmit = function(comment) { commentsRef.push(comment, function(error) { if (error === null) { actions.updateCommentCount(comment.postId, 1); } }); };

Flexibility in the Reflux PubSub Model

As Krawaller states in The Reflux data flow model:

At its heart, Reflux is really just a PubSub library. That is, we have a Publisher API for objects who transmit events, and a Subscriber API for objects who want to listen to such events.

Publisher Subscriber View Component × ✓ Store ✓ ✓ Action ✓ ×

The fact that any Subscriber can listen to any Publisher means there is some degree of flexibility. This flexibility is sometimes convenient: for simple UI events, rather than passing props down several component layers or creating another Store, I just use an Action:

var SinglePost = React.createClass({ addComment: function(e) { e.preventDefault(); if (!this.props.user.isLoggedIn) { actions.showLoginOverlay(); return; } // submit comment... }, // render, etc... });

And at the top level, I can listen to the Action directly:

var ReactNews = React.createClass({ mixins: [ Reflux.listenTo(actions.showLoginOverlay, 'showLoginOverlay') ], showLoginOverlay: function() { this.setState({ showOverlay: true }); } // etc... });

If it were any more complex, I might rather move all this state to a Store, but for my needs, this is sufficient.

Summing Up

Reflux recently reached v0.2.0, but it’s already a powerful tool for building Flux applications. For those who are looking for examples of Reflux applications, you’re welcome to check out my code on GitHub. The live demo is available here.

You can log in with:

Email: reactnews@example.com

Password: henleyedition1