Facebook Flux – Application Architecture for Building User Interfaces
github.com
github.com
We recently adopted this architecture for a medium-scale project. The unidirectional data flow has greatly reduced code complexity for us. We're still not satisfied with our server syncing strategy. Seems like actions are the best place to sync data, but how do you deal with interdependent actions?
[1] http://facebook.github.io/react/docs/flux-todo-list.html#add...
I'd also mention https://github.com/spoike/reflux as an implementation.
ClojureScrpt/OM:
https://github.com/swannodette/om (several examples linked)
http://rigsomelight.com/2014/05/01/interactive-programming-f... (flappy bird clone)
React+Mori:
???
https://github.com/jmreidy/fluxy/tree/master/examples/todomv...
I initially thought that the suggested implementation by Facebook was a bit weird because it requires a lot of boiler plate code.
For the longest time I disliked using magic strings for type checking since it breaks the concept of polymorphism. In Javascript it is even more weird considering it is a dynamic language. So I fixed that by moving the type specifics (which in Flux's case is mostly about handling events) into the actions and stores themselves.
Thanks to a contributor we also provide a mixin for React components that adds automatic unsubscription on componentWillUnmount and provides a listenTo method in the components to subscribe to actions and stores.
It works both server-side (available at npm) and client-side (available as bower package or can be built in browserify from npm package).
For instance, in the flux-chat app within the linked repo, the MessageComposer component calls `ChatMessageActionCreators.createMessage(text);` when the user press the enter key to submit a message.
To find out how that ends up affecting the application, you need to jump around more than a couple of directories to find out how it's all wired up.
I just cut out the middlemen and directly interfaced my Stores from my components. My Store then emits a change event as usual and other components that are subscribed to the change event than refetch their state. That part of Flux makes perfect sense to me. The need for Dispatchers and Actions don't.
To be fair, I didn't start looking into Flux until my app starting growing large enough that I started feeling the pain of my lack of architecture. The Stores and the event emitter solved my immediate problems. Perhaps if my app grows even larger, the Dispatcher may come in handy.
What's the practical difference between the dependency on knowing the right action to call to the dispatcher vs. the dependency on calling the Singleton Store method?
To expand a little, I have trouble identifying what the difference between these two flows are;
(something) -> store method -> change event
(something) -> "action" -> store method -> change event
Feels like actions on the dispatcher are an unnecessary level of indirection when I have to know that I'm calling a store method under the hood just by virtue of knowing the action to call. I understand batching events and things like that, but manually wiring seems like a really boilerplate heavy solution to the problem.Now that I'm writing this, I can kind of see the usefulness in making reusable components that can interact with stores. I guess I just felt like reusable components would never interacted with stores (as that's handled by controller-views which are pretty much application specific). But even then they need access to the dispatcher, and if you can inject that dependency then you can inject the store itself...
Sorry for the stream of consciousness, it's my lunch break...
As far as my understanding goes that should be fine for most use-cases. The only reason that really requires a Dispatcher would be synchronization of the incoming Store mutations.
In our app, we started feeling the growing pain of a laissez faire approach to internal comms. For us, a user interaction can generate multiple logical state changes in the app, and several related interactions can cause similar/identical changes to take place. So for us, "User Interaction->App Change" is a many-to-many relationship, and Actions help us to wire them together really well. Couple examples -
1. Opening a modal with the detail view of the item. This can happen in several ways (a user click, onload, keyboard shortcut, app event), and it involves multiple steps (ui cleanup, fetching of more info, etc). Having an actions allow us to guarantee that when we want X to happen in the app, all necessary steps will be taken regardless of what causes X or in how many places.
2. Capturing analytics. We log all key interactions for internal analysis, and some events simply do not belong in the store/ViewController/component, so we capture in within the relevant Action. Low-level events are captured within components (e.g. did they press the 'save' button or hit enter), while others are app-level actions (e.g. 'view details') and are captured within Actions. Bonus - as per the point #1 for us multiple user interactions can cause similar app changes, but by also capturing the event within Actions we can easily track all triggers - e.g. was the 'view details' caused by a clicking on a link, using a shortcut, or another app-level change.
3. Refactoring. Most of the app UI is aware of only two things - Actions and (to a lesser extent) getState() of relevant stores. This means that as long as we keep the public interfaces of those two unchanged, we can refactor or even change the implementation as much as we need. Just last week we swapped the inhouse filtering/sorting implementation a for new one based on Crossfilter which caused a comprehensive rejigging of the stores' behaviours and methods, and because we kept the same public contract it had zero impact on the UI (besides a noticeable speed improvement).
Pardon the flawed comparison, but for us actions are useful in a similar way that interfaces or MVC controllers are useful - as long as you keep them lightweight and semantic, they provide predictable and reliable endpoints for the app regardless of what turmoil happens behind the scenes.
We're also starting to see a lot of the many-to-many relationships in actions you mentioned, which convinces me we need to work towards more of a Flux architecture (it wasn't out yet when we started this lo those many months ago).
To me it makes a lot more sense for React components to push an event directly onto a pubsub event queue which then dispatches accordingly. When data changes anywhere it fires an event that then passes new state to the top level React component. Most of the actions you do to data are boilerplate and can be greatly simplified from Flux.
What am I missing? Why is it so complex?
Yes, you don't absolutely need the ActionCreator helper methods. You could call AppDispatcher.handleViewAction() or even AppDispatcher.dispatch() directly in the View. But keeping these things separated out keeps the code nicely organized and keeps application logic out of the View. Additionally, I find it helps to maintain a mental model where the only way into the data flow is through the creation of an action, codified in the library of ActionCreators.
--
The real issue I've been having React -- that I'd I like to see dealt with by a library/design pattern -- is dealing with transitions between view states. Re-rendering the view on any data change can result in very abrupt changes. For instance, my app displays multiple lists of items. When the user edits an item, that can result in the item suddenly disappearing from underneath their cursor to reappear somewhere else in that list, in a different list, or nowhere within view.
I've been able to handle each issue of this nature as it arises in an ad-hoc fashion, but I'd really like a more formalized way in React to say, "when attribute X changes, use transition Y to change from the old view state to the new one".
Does anyone know of an existing solution to this problem?
Not simply react but you can get the idea.
They write "Flux eschews MVC in favor of a unidirectional data flow", but MVC already has a unidirectional data flow (Controller -> Model -> View). Is this just a case of those who don't understand MVC are compelled to reinvent it?
EDIT: Actually, the main addition over MVC seems to be that the Stores declaratively specify their relationships between one another (which are then resolved by the dispatcher), rather than the developer writing a specific Model implementation that explicitly orders the changes to related elements of the model. I'm a bit suspicious that this would be less explicit, and so harder to maintain, but maybe I'm wrong.
http://facebook.github.io/flux/
Seems to still be in flux.
I had originally used Backbone.Model and Backbone.Collection in conjunction with React. But after reading about Flux on the React blog and watching the video explanation back in May, it only took me a week to replace all of my Backbone models and collections with Flux singleton-stores. Coupled with Andrey Popp's react-router-component[0] (now replaced by rrouter[1], I think?), I was able to remove Backbone from the project entirely.
I think that Flux is more flexible than Backbone. I really like the ability for a store to take on characteristics of both models and collections. It is also easy to combine many different external resources to compose the "one true source" of a particular type of data for many different components. I don't feel any particular need to bring back a Backbone dependency in future React projects.
At least Andrey add his reasoning for the latest deprecation in rrouter's documentation. Although the page regarding server-side regarding in the rrouter docs is empty :(
According to the docs "RRouter was developed to replace react-router-component and fix design flaws introduced by it." Maybe I'm just unfamiliar with the npm community but is it typical to just rename projects once they introduce breaking changes?
That said, like most new architectures/frameworks finding a big example (not just a TODO app) is really hard. We are currently prototyping a big app using React/Flux and we find ourselves having to question ourselves a lot more than we'd like.
Once we finally have a good, solid, stable UI building consensus in HTML5/JS, it'll not only be possible to use it for the web but for the desktop too via node-webkit / atom-core.
I've been thinking about this and concluding that React wouldn't benefit even a little from those FRP libraries and architecture, it is already quite functional reactive. Am I wrong?
Why does big companies do that? Just recently Apple's Swift and now Facebook's Flux.
[1] http://en.wikipedia.org/wiki/Go!_%28programming_language%29