Recently we've been building applications using the excellent React JS. We're now at a point where we've graduated to using Flux as an application pattern along side React, bringing more structure to our applications.
Flux is an excellent pattern, but most of the examples I've found to date have been fairly basic and don't accurately explain how to scale the pattern to more complex applications with multiple stores reading and writing data to remote sources. This post is a collection of tips and examples I've found along the way that will hopefully help others on their Flux adventures.
Just as a note, if you're not familiar with Flux or React I recommend you take a look at some introductory material before looking through this post. If you need an introduction to Flux, I can recommend this one from Brigade.
- As a general rule of thumb, you should have one store per entity type in your application; the example chat app from FB is a good reference point.
- Stores don't have to just represent data, they can also represent application state; whether modals are shown / hidden, if the user is online / offline etc.
- Stores can, and probably will need to use data from other stores. If your store relies on another store for updating it's content then you'll need to rely on the waitFor method in the dispatcher. A great example is in the FB Chat app, where the 'UnreadThreadStore' listens for changes from the 'ThreadStore' and the 'MessageStore'
- Stores can also represent relationships to other data; @gaeron calls these 'ListStores' in his excellent 'flux-react-router-example' application. A ListStore may be comprised of a list of IDs for an entity contained in another store which can be retrieved by looping over the IDs and fetching the data from the entity store (e.g. user_ids -> UserStore.get)
- Storing partial object data in a store may be problematic. For example, if you have an application that fetches photos and a photo result has a partial entry for every user who has liked that photo, storing those users optimistically when the photo data comes in means you're going to still need to fetch each one of those users. A store should maintain consistent state for each entity inside of it for simplicity.
- Stores should not contain the logic to fetch themselves on a server; far better is using DAO (Data Access Objects) that are thinly wrapped API objects to get the data
- Actions should be split into two types; view actions and server actions. This separates user interactions, such as clicking a button from retrieving data.
- Actions should be 'fire and forget' and should not have callbacks. If you need to respond to the result of an action, you should be listening for a completion or error event. This enforces data being kept in the store and not on a component.
- Actions may need context objects, that help tie actions back to specific components. Ian Obermiller mentions this in his comments on HackerNews as a practice they implemented at FB to allow components to be know whether they should update or not.
- Keep them in one file until you have too many; multiple files can quickly become a lot to handle
- Namespace your constants under an entity; this will make it easier to break them out into multiple files later if needed.
- Events to notify the components that fetching has started can be beneficial, and other stores can optimistically update (if needed) with these events. The flux-react-router-example has a good example of well named constants here.
- Keep React conventions; keep state in a top level component and let children re-render on changes to that state.
- Stateful components should subscribe to whatever stores they need to update themselves. Don't be afraid for a component to listen to multiple stores; it's perfectly fine!
- Use a mixin for listening to stores in a component; @gaeron has a great one here that you can re-use.
Remote Data Fetching
I feel like this subject requires its own section. Looking at the Flux examples out there, people seem pretty divided about where the logic should go for fetching data and which items responsibility it is to provide this.
Ian Obermiller goes into detail in a transcript of an interview of how they approach this at Facebook (it's well worth a read). To summarize that post; they use Entity Stores that know how to update themselves via Data Access Objects called directly from a component. This kind of goes against some of the logic I've seen in the past, but moves more towards the approach that Relay is looking at – components declaring what data they need to render, except that in this instance it would be at a higher level.
I've personally had some success with APIs being called through an Action on a component. Here's a rough example of what I'm referring to:
However, this does add a layer of indirection and may add some logic for fetching into your action. For example, what if you now want a layer of caching? Does that now live in the action that dispatches the API request? The (heavily) mentioned 'flux-react-router-example' from @gaeron certainly goes down this path, but I'm inclined to believe application fetching logic should not live in your actions.
To quote Ian in the aforementioned transcript about the FB approach:
Ian Obermiller, 2014
Actually, to clarify, there is no action to fetch data. We don’t have any action type that’s like fetch messages or anything like that. The store takes care of all the fetching for you. It might be doing it from a cache. It might be actually contacting the server. It might be making multiple server calls just to get the data you need, stuff like that.
The only downside I can see (and as Ian points out), you start to merge responsibilities for the store. Initially I really like the idea of the store being completely de-coupled to the fetching and frowned at approaches that conflate the two, but I can certainly see advantages in that approach.
All the references cited in this example, as well as a few other bonus links can be found here:
- Flux React Router Example
- Interview with Ian Obermiller
- Flux Chat example application
- Flux structuring comments on HN from Ian Obermiller
- Async requests with React.js and Flux, revisited.
- Dispatcher Overview
- Flux Introduction at F8
- Testing Flux Applications
- Flux Shopping cart example
- What is the Flux Application Architecture
Now on Kickstarter: https://www.kickstarter.com/projects/hackaball/hackaball-a-programmable-ball-for-active-and-creatLast year we made a working prototype ...
So you want to start usability testing, don’t where to start finding testers. Have you tried Taskrabbit?