Slide 1

Slide 1 text

One Man’s Adventure at 1.21 GiGAwATtS FlUX @jaketrent

Slide 2

Slide 2 text

What is FlUX A pattern for organizing state in your React.js apps ?

Slide 3

Slide 3 text

Where does the 1.21 gIGaWAtTS come from? npm install flux

Slide 4

Slide 4 text

Where does the 1.21 gIGaWAtTS Included: Dispatcher Invariant come from? Your Code: Stores Actions ApiHelper Components Router

Slide 5

Slide 5 text

PlUToNIuM All you need is a little The parts of Flux…

Slide 6

Slide 6 text

Web APIs API Helpers Actions Dispatcher Stores Components ThE CoDE CoNCePTs DaTA FlOW and

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Web APIs API Helpers Dispatcher Stores AcTIoNS What the app can *do* Components Actions • Wrapped action payload makes all dispatched methods consistent

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Web APIs API Helpers Actions Stores DiSPaTChER Global message broker Components Dispatcher • Dispatches all payloads to all stores

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

All together, data flow should be UnIDiREcTIoNAl Web APIs API Helpers Actions Dispatcher Stores Components

Slide 22

Slide 22 text

Let’s look at a couple CrUD Flows - Immediately applicable, write a lot of them - Exercise all the parts Examples: a books administration app

Slide 23

Slide 23 text

FiLE StRUcTUrE books-actions.js books-api.js books-constants.js books-list.js books-store.js books/ common/ api.js app-dispatcher.js node_modules/ flux/ react-router/

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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(, document.body) 3 BooksAction.transition(state.params) 4 })

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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(, document.body) 3 BooksAction.transition(state.params) 4 })

Slide 33

Slide 33 text

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 }

Slide 34

Slide 34 text

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 }

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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 },

Slide 39

Slide 39 text

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 }

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

AnECdOTeS from my use of the pattern…

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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)

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

ThANk yOU github.com/jaketrent/jaketrent-admin - admin crud github.com/jaketrent/jaketrent-books - simple consumer CoDE SlIDeS speakerdeck.com/jaketrent/flux-at-1-dot-21-gigawatts