From AngularJS to Flux - The React Way
Asked Answered
I

1

31

As a developer with good hands-on AngularJS experience, how do I adjust my mental model of writing web apps in Flux using React?

I'm not looking for a Flux+React vs Angular answer (already plenty of that online), but I would like to know what are the biggest differences in the two "mindsets": beforehand, I was introduced into The Angular Way; comparatively, what is The React Way ?

As I leave the Angular universe and transition to Flux, what are the key things I need to start paying attention to?

Differences first, and now similarities: AngularJS is very opinionated and had some very big no-no's, like - don't put UI/DOM code in controllers. What are the big no-no's and opinions of React?

Last but not least, Facebook refers to Flux as an alternative to MVC, but as I am looking at it - React is the view engine, stores are model containers focused on a single domain, and the dispatcher and actions form a controller. So isn't this actually MVC with a different name?

Incunabula answered 3/12, 2014 at 5:43 Comment(5)
I don't have experience with Angular, so it's hard to explain differences and transition process between two of them. However regarding second part of question: React is simply V from MVC and I would compare it with Mustache / Marionette. Flux has stores and dispatcher and together with React both of them creates MVCish structure. Also you can quite easily replace Flux with Backbone.Ginkgo
In my opinion, Flux is an anti-pattern. Coupling all stores to a single dispatcher regardless of their level of abstraction and responsibility leads to a massive dispatcher block with a nightmare of management.Revelationist
@DmitriZaitsev Flux does not impose the requirement to only have one dispatcher per page. You can have as many dispatcher as practical handling different types of actions on the same page.Tolidine
@Tolidine Isn't that an anti-pattern? Considering the debugging benefits of funnelling all actions through a single dispatcher?Incunabula
Why is that an anti-pattern? Does it really cause that much more debugging overhead to have 2 or 3 dispatchers vs 1? In my experience, it is usually best to isolate components to as small a footprint as possible, especially an event bus. Having one big global event bus means your actions/events are propagated all over the place, increasing the likelihood of collisions and unexpected behaviors. Also, see @DmitriZaitsev's comment on a massive dispatcher block.Tolidine
L
53

I'll let others do the compare/contrast with Angular, but here are some answers to two of your questions.

So isn't this actually MVC with a different name?

The presence in Flux of a separation of concerns between the data/logic layer and the view layer does not make it MVC. Many other patterns have a similar split, most notably CQRS, arguably Flux's closest cousin. There is no controller in Flux, in the MVC sense. The Actions and Dispatcher do not amount to a controller. The Controller-views are close, but are actually quite limited in their controller-like aspect. A key difference is that MVC controllers contain application logic and act upon models. Flux stores, by contrast, contain all the application logic and have no setters.

As I leave the Angular universe and transition to Flux, what are the key things I need to start paying attention to?

Key values of Flux:

  • Simplicity over complexity.
  • The mental model of the programmer is at least as important as the code itself.
  • Parts of the application should be highly decoupled and "know" as little about the other parts as possible.
  • Inversion of control: all control should reside in the stores, where state is managed. Stores are not acted upon, but rather informed by actions.
  • Applications should stay resilient and flexible as they grow, allowing for new, unexpected features to be developed more quickly and easily.

Key concepts in Flux:

  • Unidirectional data flow: Action → Dispatcher → Stores → Views
    • Every change in state and all new data begins with a dispatched Action.
    • This four-step data flow is the core mental model for the Flux programmer.
  • A dispatch causes an application-wide transformation of application state.
    • This is a moment in time, creating a snapshot of change. This is easy to reason about.
    • We cannot dispatch while dispatching and preserve this simplicity. Thus, any corollary change must be driven by the original action.
  • Flux stores are domain models, not ORM models. They contain all logic and manage all state for a particular logical domain within the application. They may contain collections, singular values or a combination of both.
  • Flux assumes that derived data -- where one store depends on changes in another store -- is an eventuality in complex applications where models or stores manage normalized data. To deal with this, a Flux Dispatcher should expose a mechanism to declaratively manage this dependency within the store. In Facebook's Dispatcher, this is done with the waitFor() method. This allows one store to wait for another store's response to an action before moving forward with its own response.

Primary parts of a Flux application:

  • Actions: an object literal containing new data and a specific type, allowing Stores to discern whether it is relevant to their domain.
  • Dispatcher: a registry of callbacks that, by way of the callbacks, distributes a payload (an Action) to the registrants (the Stores). It has no intelligence of its own. All intelligence is in the Stores.
  • Stores: a domain model which registers itself with the Dispatcher and emits a 'change' event whenever a change in its state occurs.
  • Controller-views: view components near the root of the component tree. They listen for 'change' events in stores and respond to this event by retrieving new data through the store's exposed getter methods and passing it to their children. The only difference between Controller-views and Views is this listening functionality.
  • Views: stateless children of the controller-view components, receiving and passing along data, much like pure functions. Views and Controller-views are most often implemented with React, as it provides the ability to re-render at will with very little performance loss.
  • Utils: libraries of pure functions that can be easily shared across different modules.

Overview, in depth: http://facebook.github.io/flux/docs/overview.html

Tutorial: http://facebook.github.io/flux/docs/todo-list.html

Examples: https://github.com/facebook/flux/tree/master/examples

Actions and the Dispatcher: http://facebook.github.io/react/blog/2014/07/30/flux-actions-and-the-dispatcher.html

Testing: http://facebook.github.io/react/blog/2014/09/24/testing-flux-applications.html

More out in the wild: http://facebook.github.io/react/blog/2014/10/17/community-roundup-23.html

Lipography answered 3/12, 2014 at 8:41 Comment(10)
Thank you. Could you please expand on key concept #4? Specifically regarding this: "Flux assumes that derived data -- where one store depends on changes in another store -- is an eventuality in complex applications where models or stores manage normalized data." (the mechanism part is clear). Thanks.Incunabula
When an application has normalized data, that is when it doesn't duplicate data in two places (because that would be difficult to maintain), we often run into a situation where the data in Store A is needed in Store B. For example, an ImageStore vs. ThemeStore in an app that builds a greeting card. Each theme can contain a different maximum number of images. The ImageStore needs to know which theme is selected, so it knows how many it can mark as valid. When the action of type THEME_SELECTED is dispatched, ImageStore needs to waitFor ThemeStore, and then get the selected theme's max.Lipography
Dealing with derived data was actually the original reason why Flux was developed at Facebook. Lots of benefits later emerged, however.Lipography
Thanks! I did see that lecture where Jing commented on difficulties they where having with derived data, but didn't make the connection. May I also ask whether data normalization is done on the server side or client side? Maybe it's a given (client side?) but I'm not so confident to answer this myself.Incunabula
Your comment - please?Incunabula
By "normalization", I am referring to the idea that stores in Flux do not contain duplicate copies of the same data. We don't denormalize for the same reason people don't do it in a relational database -- it's difficult to keep in sync, and causes a lot of complexity to manage. This is purely client-side.Lipography
To be more clear -- denormalization is another strategy for dealing with the same problem of derived data. If you keep the state in multiple places, one store does not need to wait for another. I have talked with people (not at Facebook) who have advocated for this, but I respectfully disagree with that idea.Lipography
I fully stand behind the concept of absolutely avoiding derived data, and normalizing everything (ie, one store per logical domain, with no duplicates). However, what if there is a domain which several stores relate to - such as basic user info (name, profile picture, etc)?Incunabula
Not sure what you're asking. If the related stores need to retrieve the user data from the UserStore, they would want to do that after a call to waitFor(), to make sure they are receiving the most recent data.Lipography
Thanks. I was travelling, only now read your reply. I think I get it now.Incunabula

© 2022 - 2024 — McMap. All rights reserved.