Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Flux at 1.21 Gigawatts

Jake Trent
January 27, 2015

Flux at 1.21 Gigawatts

Here I present how I organize the elements of the Flux architecture in my mind, show how some of my Flux code has turned out, and make some observations of what I see as the good and bad of Flux. Where we're going, we don't need roads.

Jake Trent

January 27, 2015
Tweet

More Decks by Jake Trent

Other Decks in Technology

Transcript

  1. Where does the 1.21 gIGaWAtTS Included: Dispatcher Invariant come from?

    Your Code: Stores Actions ApiHelper Components Router
  2. Web APIs API Helpers Actions Dispatcher Stores CoMPoNEnTS User interface

    IN (from Stores) Change events Model data OUT (to Actions) Calls w/ UI data Components
  3. Web APIs API Helpers Dispatcher Stores AcTIoNS What the app

    can *do* IN (from Components) Calls w/ UI data OUT (to API Helpers) Data to persist Components Actions
  4. Web APIs API Helpers Actions Dispatcher Stores ApI HeLPeRS API

    data access layer IN (from Actions) Data to persist OUT (to Web API) Network requests Components
  5. Web APIs API Helpers Actions Dispatcher Stores ApI HeLPeRS API

    data access layer IN (from Web APIs) Network responses OUT (to Actions) Data from APIs Components
  6. Web APIs API Helpers Dispatcher Stores AcTIoNS What the app

    can *do* IN (from API Helpers) Data from APIs OUT (to Dispatcher) Wrapped action payloads Components Actions
  7. Web APIs API Helpers Dispatcher Stores AcTIoNS What the app

    can *do* Components Actions • Wrapped action payload makes all dispatched methods consistent
  8. Web APIs API Helpers Actions Stores DiSPaTChER Global message broker

    IN (from Actions) Wrapped action payloads OUT (to Stores) Wrapped action payloads Components Dispatcher
  9. Web APIs API Helpers Actions Stores DiSPaTChER Global message broker

    Components Dispatcher • Dispatches all payloads to all stores
  10. Web APIs API Helpers Actions Dispatcher StOReS Holds domain model

    data on the client IN (from Dispatcher) Wrapped action payloads OUT (to Components) Change events Model data Components Stores
  11. Web APIs API Helpers Actions Dispatcher StOReS Holds domain model

    data on the client Components • Listens for only dispatched payloads it cares about • Can defer payload handling by waiting for other stores • Cohesive around a domain • Can have multiple Stores
  12. Web APIs API Helpers Actions Dispatcher StOReS “When should I

    make another store?” Components Stores • Circular dependencies • Multiple domains
  13. Web APIs API Helpers Actions Dispatcher StOReS “When should I

    make another store?” Components Stores If you discover that there are actually multiple, non- overlapping domains within that store, you have a good case for breaking it into separate stores. - Bill Fisher
  14. Web APIs API Helpers Actions Dispatcher Stores CoMPoNEnTS User interface

    IN (from Stores) Change events Model data OUT (to Actions) Calls w/ UI data Components
  15. Web APIs API Helpers Actions Dispatcher RoUTeR Matches URL changes

    to code IN (from URL) Requested application state OUT (to Components) URL state Stores Components Router OUT (to Actions) URL state
  16. All together, data flow should be UnIDiREcTIoNAl Web APIs API

    Helpers Actions Dispatcher Stores Components
  17. Let’s look at a couple CrUD Flows - Immediately applicable,

    write a lot of them - Exercise all the parts Examples: a books administration app
  18. ShOW Flow Router transitions Store saves data, emits change Api

    Helper calls api, success action Action dispatches Component gets store data, renders Action
 calls api helper
  19. ShOW Flow Router transitions Store saves data, emits change Action


    calls api helper Api Helper calls api, success action Action dispatches Component gets store data, renders 1 Router.run(routes, (Handler, state) => { 2 React.render(<Handler />, document.body) 3 BooksAction.transition(state.params) 4 })
  20. ShOW Flow Router transitions Store saves data, emits change Api

    Helper calls api, success action Action dispatches Component gets store data, renders 1 exports.transition = (filter) => { 2 if (!BooksStore.find(filter)) 3 BooksApi.fetch(null, filter) 4 } Action
 calls api helper
  21. ShOW Flow Router transitions Store saves data, emits change Api

    Helper calls api, success action Action dispatches Component gets store data, renders 1 exports.fetch = (url, filter) => { 2 request.get(url).end((err, res) => { 3 // .. 4 BooksActions.fetchSuccess(res.body.books) 5 }) 6 } Action
 calls api helper
  22. ShOW Flow Router transitions Store saves data, emits change Api

    Helper calls api, success action Action dispatches Component gets store data, renders 1 exports.fetchSuccess = (models, filter) => { 2 AppDispatcher.handleServerAction({ 3 type: ActionTypes.FETCH_SUCCESS, 4 models: models, 5 filter: filter 6 }) 7 } Action
 calls api helper
  23. ShOW Flow Router transitions Store saves data, emits change Api

    Helper calls api, success action Action dispatches Component gets store data, renders 1 BooksStore.dispatchToken = AppDispatcher.register((payload) => { 2 var action = payload.action 3 switch(action.type) { 4 5 case ActionTypes.FETCH_SUCCESS: 6 cache(action.models) 7 BooksStore.emitChange() 8 break Action
 calls api helper
  24. ShOW Flow Router transitions Store saves data, emits change Api

    Helper calls api, success action Action dispatches Component gets store data, renders 1 componentDidMount() { 2 BooksStore.addChangeListener(this._onChange) 3 }, 4 5 _onChange() { 6 this.setState({ 7 book: BooksStore.find({ id: this.getParams().id }) 8 }) 9 }, Action
 calls api helper
  25. CrEAtE PaGE Flow Router transitions Component gets store data renders

    Component calls create select action Action dispatches Store inits data, emits change
  26. CrEAtE PaGE Flow Router transitions Component gets store data renders

    Component calls create select action Action dispatches Store inits data, emits change 1 Router.run(routes, (Handler, state) => { 2 React.render(<Handler />, document.body) 3 BooksAction.transition(state.params) 4 })
  27. CrEAtE PaGE Flow Router transitions Component gets store data renders

    Component calls create select action Action dispatches Store inits data, emits change 1 componentDidMount() { 2 BooksActions.createSelect() 3 }
  28. CrEAtE PaGE Flow Router transitions Component gets store data renders

    Component calls create select action Action dispatches Store inits data, emits change 1 exports.createSelect = () => { 2 AppDispatcher.handleViewAction({ 3 type: ActionTypes.CREATE_SELECT 4 }) 5 }
  29. CrEAtE PaGE Flow Router transitions Component gets store data renders

    Component calls create select action Action dispatches Store inits data, emits change 1 BooksCreateStore.dispatchToken = AppDispatcher.register((payload) => { 2 var action = payload.action 3 switch(action.type) { 4 5 case ActionTypes.CREATE_SELECT: 6 _book = {} 7 break
  30. CrEAtE PaGE Flow Router transitions Component calls create select action

    Action dispatches Store inits data, emits change 1 componentDidMount() { 2 BooksStore.addChangeListener(this._onChange) 3 }, 4 5 _onChange() { 6 this.setState({ 7 book: BooksCreateStore.getBook() 8 }) 9 }, Component gets store data renders
  31. CrEAtE SaVE Flow Component handles submit, create action Action calls

    api helper Api Helper calls api ,success action Action dispatches Store saves data/flag, emits change Component gets store data, renders, transitions
  32. CrEAtE SaVE Flow Component handles submit, create action Action calls

    api helper Api Helper calls api ,success action Action dispatches Store saves data/flag, emits change Component gets store data, renders, transitions 1 onSubmit(evt) { 2 evt.preventDefault() 3 BooksActions.create({ 4 title: React.findDOMNode(this.refs.title).value, 5 // ... 6 }) 7 },
  33. CrEAtE SaVE Flow Component handles submit, create action Action calls

    api helper Api Helper calls api ,success action Action dispatches Store saves data/flag, emits change Component gets store data, renders, transitions 1 exports.create = (model) => { 2 BooksApi.create(model) 3 }
  34. CrEAtE SaVE Flow Component handles submit, create action Api Helper

    calls api ,success action Action dispatches Store saves data/flag, emits change Component gets store data, renders, transitions 1 exports.create = (url, model) => { 2 request.post(url).send({ books: model }) 3 .end((err, res) => { 4 // .. 5 BooksActions.createSuccess(res.body.books) 6 }) 7 } Action calls api helper
  35. CrEAtE SaVE Flow Component handles submit, create action Api Helper

    calls api ,success action Action dispatches Store saves data/flag, emits change Component gets store data, renders, transitions 1 exports.createSuccess = (model) => { 2 AppDispatcher.handleServerAction({ 3 type: ActionTypes.CREATE_SUCCESS, 4 model: model 5 }) 6 } Action calls api helper
  36. CrEAtE SaVE Flow Component handles submit, create action Api Helper

    calls api ,success action Action dispatches Store saves data/flag, emits change Component gets store data, renders, transitions 1 BooksCreateStore.dispatchToken = AppDispatcher.register((payload) => { 2 var action = payload.action 3 switch(action.type) { 4 5 case ActionTypes.CREATE_SUCCESS: 6 _book = action.model 7 _isCreated = true 8 BooksCreateStore.emitChange() 9 break Action calls api helper
  37. CrEAtE SaVE Flow Component handles submit, create action Api Helper

    calls api ,success action Action dispatches Store saves data/flag, emits change Component gets store data, renders, transitions 1 componentDidMount() { 2 BooksCreateStore.addChangeListener(this._onChange) 3 }, 4 5 _onChange() { 6 this.setState(this.getStateFromStores(), () => { 7 if (BooksCreateStore.isCreated()) { 8 Router.transitionTo('books-show', { id: this.state.book.id }) 9 } 10 }) 11 }, Action calls api helper
  38. CoMPoNEnTS - So clean - Just views again - Feels

    like all the right stuff has been moved out - Stores make it easier to state in more places
  39. AcTIoNS - Sometimes seems like a superfluous layer - Has

    value in consistency - Helps with Law of Demeter - Nice translation from method calls (good in views) to dispatch calls (knowledge of dispatchers and action types)
  40. StOReS - Sync methods are awesome — can assume the

    data is there — makes view usage of stores clean - `waitFor` is helpful — great for dependent data in separate logical stores - Love not having setters on Store and letting data mutation flow through dispatcher callback
  41. CoMPlEXiTY - There are many parts. Some of the simplicity/

    beauty of the React component abstraction is diminished in the fray - Lots of code in multiple files — mostly declarative, ok to reason about by itself, harder to see connections
  42. DaTA FlOW - Generally feels like single direction. Once the

    data is signaled to be available in view, it’s consistent - Still sometimes a challenge to follow the data previous to that
  43. EvENtS - Harder to follow source of actions/data and know

    what’s affected - Requires consistency in a pattern - Multiple event sets to track 1) Stores register callbacks for dispatcher calls 2) Views register as listeners to store events
  44. ApPRoACh - Experiment with `isCreated` flags. Feels clunky
 - Remove

    metadata (eg, paging) from Stores; Actions won’t need to talk to Stores - Some of my own abstractions for Actions and Stores - Play with some of the Flux implementations How I might experiment with my
  45. LeARnINg CuRVe - More full-featured, real-world examples continue to be

    made - Feels like Backbone days — figure out how Flux works well for you - Relay/GraphQL are incoming
  46. OtHEr FlUX LiBS - Fluxxor - Reflux - Delorean -

    Fluxxy - marty.js - Fluxible - …check HN Generally clean up some boilerplate, abstract a few things behind a smoother interface
  47. Videos / Tuts - facebook.github.io/flux - docs, examples, blog, vids

    - egghead.io/series/learn-react-flux - walkthrough vid Comparisons - flux implementation sites - compare/contrast blog - github.com/yhagio/learn-react-flux - compilation - github.com/voronianski/flux-comparison - impls LeARnINg ReSOuRCeS