React.js and the Flux are shaping up to be some of the most important tools for web development in the coming years. The MVC model was strong on the server when we decided to take the frontend seriously, and it was shoehorned into the frontend since we didn’t know any better. React and Flux challenge that and I like where it’s going very much. That being said, it was very difficult for me to get into. I put together this blog post to serve as a more practical guide - the upstream documentation tells you a lot of concepts and expects you to put them together yourself. Hopefully at the end of this blog post you can confidently start writing things with React+Flux instead of reading brain-melting docs for a few hours like I did.
Flux is based on a unidirectional data flow. The direction is: dispatcher ➜ stores ➜ views, and the data is actions. At the stores or views level, you can give actions to the dispatcher, which passes them down the line.
Let’s explain exactly what piece is, and how it fits in to your application. After this I’ll tell you some specific details and I have a starter kit prepared for you to grab as well.
The dispatcher is very simple. Anything can register to receive a callback when an “action” happens. There is one dispatcher and one set of callbacks, and everything that registers for it will receive every action given to the dispatcher, and can do with this as it pleases. Generally speaking you will only have the stores listen to this. The kind of actions you will send along may look something like this:
- Add a record
- Delete a record
- Fetch a record with a given ID
- Refresh a store
Anything that would change data is going to be given to the dispatcher and passed along to the actions. Since everything receives every action you give to the dispatcher, you have to encode something into each action that describes what it’s for. I use objects that look something like this:
... is whatever extra data you need to include (the ID of the record
to fetch, the contents of the record to be added, the property that needs to
change, etc). Here’s an example payload:
The Accounts store is listening for actions that start with
ACCOUNTS. and when
CREATE.USER, it knows a new user needs to be created with these
The stores just have ownership of data and handle any changes that happen to that data. When the data changes, they raise events that the views can subscribe to to let them know what’s up. There’s nothing magic going on here (I initially thought there was magic). Here’s a really simple store:
Yeah, that’s all there is to it. Each store should be a singleton. You use it like this:
Stores end up having a lot of boilerplate. I haven’t quite figured out the best way to address that yet.
Views are react components. What makes React components interesting is that they
re-render the whole thing when you call
setState. If you want to change the
way it appears on the page for any reason, a call to
setState will need to
happen. And here are the two circumstances under which they will change:
- In response to user input to change non-semantic view state
- In response to a change event from a store
The first bullet here means that you can call
setState to change view states,
but not data. The second bullet is for when the data changes. When you change
view states, this refers to things like “click button to reveal form”. When you
change data, this refers to things like “a new record was created, show it”, or
even “a single property of a record changed, show that change”.
Wrong way: you have a text box that updates the “name” of a record. When the user presses the “Apply” key, the view will re-render itself with the new name.
Right way: When you press “Apply”, the view sends an action to the dispatcher to apply the change. The relevant store picks up the action, applies the change to its own data store, and raises an event. Your view hears that event and re-renders itself.
- Easy to have stores depend on each other
- All views that depend on the same stores are updated when it changes
- It follows that all cross-store dependencies are updated in a similar fashion
- Single source of truth for data
- Easy as pie to pick up and maintain with little knowledge of the codebase
Here are some problems I ran into, and the fluxy solution to each.
Need to load data async
- When you use a store, call
- When you pull data from the store, expect
nulland handle this elegantly.
- When the initial fetch finishes in the store, raise a change event.
fetchIfNecessary in the store, go do the request unless it’s in progress or
done. On the view side, show a loading spinner or something if you get
When the change event happens, whatever code set the state of your component
initially will be re-run, and this time it won’t get
null - deal with it
appropriately (show the actual UI).
This works for more than things that are well-defined at dev time. If you need to, for example, fetch data for an arbitrary ID:
- View calls
null, renders lack of data appropriately
- Store is like “my bad” and fetches it from the server
- Store raises change event when it arrives and the view re-renders
Batteries not included
Upstream, in terms of actual usable code, flux just gives you a dispatcher. You also need something to handle your events. This is easy to roll yourself, or you can grab one of a bazillion things online that will do it for you. There is also no base Store class for you, so make one of those. You should probably just include some shared code for raising events and consuming actions. Mine looks something like this:
Do what works best for you.
If you want something with the batteries in and a base to build from, I’ve got you covered. Head over to SirCmpwn/react-starter-kit on Github.
React and Flux are going to be big. This feels like the right way to build a frontend. Hopefully I saved you from all the headache I went through trying to “get” this stuff, and I hope it serves you well in the future. I’m going to be pushing pretty hard for this model at my new gig, so I may be writing more blog posts as I explore it in a large-scale application - stay tuned.