Slide 1

Slide 1 text

React and Flux Building Applications with a Unidirectional Data Flow

Slide 2

Slide 2 text

React and Flux Building Applications with a Unidirectional Data Flow Bill Fisher @fisherwebdev

Slide 3

Slide 3 text

React and Flux Bill Fisher @fisherwebdev https://speakerdeck.com/fisherwebdev/flux-react http://facebook.github.io/flux/ http://facebook.github.io/react/

Slide 4

Slide 4 text

Bill Fisher @fisherwebdev #reactjs #fluxjs https://speakerdeck.com/fisherwebdev/flux-react http://facebook.github.io/flux/ http://facebook.github.io/react/

Slide 5

Slide 5 text

https://speakerdeck.com/fisherwebdev/flux-react

Slide 6

Slide 6 text

https://speakerdeck.com/fisherwebdev/flux-react

Slide 7

Slide 7 text

Model Model Model Model

Slide 8

Slide 8 text

Model Model Model Model

Slide 9

Slide 9 text

Model Model Model Model

Slide 10

Slide 10 text

Model Model Model Model

Slide 11

Slide 11 text

Model Model Model Model

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Dispatcher Action Store View

Slide 18

Slide 18 text

THE PLAN Anatomy of a React + Flux Application Actions & Action Creators The Dispatcher Stores + Testing Views, Controller-views and React Initialization & Interacting with a Web API

Slide 19

Slide 19 text

FILES Primary: action creators, dispatcher, stores, React components Secondary: bootstrap, utilities, constants

Slide 20

Slide 20 text

.   ├──  css   │      └──  app.css   ├──  index.html   ├──  js   │      ├──  actions   │      │      └──  FooActionCreators.js   │      ├──  AppBootstrap.js   │      ├──  AppConstants.js   │      ├──  AppDispatcher.js   │      ├──  stores   │      │      ├──  FooStore.js   │      │      └──  __tests__   │      │              └──  FooStore-­‐test.js   │      ├──  utils   │      │      ├──  AppWebAPIUtils.js   │      │      ├──  FooUtils.js   │      │      └──  __tests__   │      │              ├──  AppWebAPIUtils-­‐test.js   │      │              └──  FooUtils-­‐test.js   │      └──  views   │              ├──  FooControllerView.react.js   │              └──  FooChild.react.js   └──  package.json

Slide 21

Slide 21 text

js   ├──  actions   │      └──  FooActionCreators.js   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──  AppDispatcher.js   ├──  stores   │      ├──  FooStore.js   │      └──  __tests__   │              └──  FooStore-­‐test.js   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──  views          ├──  FooControllerView.react.js          └──  FooChild.react.js .   ├──  css   │      └──  app.css   ├──  index.html   ├──   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   └──  package.json

Slide 22

Slide 22 text

js   ├──  actions   │      └──  FooActionCreators.js   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──  AppDispatcher.js   ├──  stores   │      ├──  FooStore.js   │      └──  __tests__   │              └──  FooStore-­‐test.js   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──  views          ├──  FooControllerView.react.js          └──  FooChild.react.js

Slide 23

Slide 23 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  FooActionCreators.js AppDispatcher.js stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js views   ├──  FooControllerView.react.js   └──  FooChild.react.js

Slide 24

Slide 24 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  FooActionCreators.js AppDispatcher.js stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js views   ├──  FooControllerView.react.js   └──  FooChild.react.js Dispatcher Action Store View

Slide 25

Slide 25 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  FooActionCreators.js AppDispatcher.js stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js views   ├──  FooControllerView.react.js   └──  FooChild.react.js Dispatcher Action Store View

Slide 26

Slide 26 text

actions   └──  FooActionCreators.js Action ACTIONS & ACTION CREATORS

Slide 27

Slide 27 text

ACTIONS & ACTION CREATORS Actions: an object with a type property and new data Action creators: semantic methods that create actions collected together in a module to become an API

Slide 28

Slide 28 text

//  FooActionCreators.js   ! var  AppDispatcher  =  require('../AppDispatcher');   var  AppConstants  =  require('../AppConstants');   ! var  ActionTypes  =  Constants.ActionTypes;   ! module.exports  =  {   !    createMessage:  function(text)  {          AppDispatcher.dispatch({              type:  ActionTypes.MESSAGE_CREATE,              text:  text          });      }   ! };

Slide 29

Slide 29 text

!    createMessage:  function(text)  {          AppDispatcher.dispatch({              type:  ActionTypes.MESSAGE_CREATE,              text:  text          });      }

Slide 30

Slide 30 text

!    createMessage:  function(text)  {          AppDispatcher.dispatch({              type:  ActionTypes.MESSAGE_CREATE,              text:  text          });      }

Slide 31

Slide 31 text

!    createMessage:  function(text)  {          AppDispatcher.dispatch({              type:  ActionTypes.MESSAGE_CREATE,              text:  text          });      }

Slide 32

Slide 32 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  FooActionCreators.js AppDispatcher.js stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js views   ├──  FooControllerView.react.js   └──  FooChild.react.js Dispatcher Action Store View

Slide 33

Slide 33 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  FooActionCreators.js AppDispatcher.js stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js views   ├──  FooControllerView.react.js   └──  FooChild.react.js Dispatcher Action Store View

Slide 34

Slide 34 text

THE DISPATCHER AppDispatcher.js Dispatcher

Slide 35

Slide 35 text

THE DISPATCHER Essentially a registry of callbacks To dispatch, it invokes all the callbacks with a payload Flux dispatcher is a singleton; payload is an action Primary API: dispatch(), register(), waitFor() Base class is available through npm or Bower.

Slide 36

Slide 36 text

//  AppDispatcher.js   ! var  Dispatcher  =  require('Flux.Dispatcher');   ! //  export  singleton   module.exports  =  new  Dispatcher();  

Slide 37

Slide 37 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  FooActionCreators.js AppDispatcher.js stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js views   ├──  FooControllerView.react.js   └──  FooChild.react.js Dispatcher Action Store View

Slide 38

Slide 38 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  FooActionCreators.js AppDispatcher.js stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js views   ├──  FooControllerView.react.js   └──  FooChild.react.js Dispatcher Action Store View

Slide 39

Slide 39 text

Store stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js STORES

Slide 40

Slide 40 text

STORES Each store is a singleton The locus of control within the application Manages application state for a logical domain Private variables hold the application data Numerous collections or values can be held in a store

Slide 41

Slide 41 text

STORES Register a callback with the dispatcher Callback is the only way data gets into the store No setters, only getters: a universe unto itself Emits an event when state has changed

Slide 42

Slide 42 text

//  FooStore.js   ! var  _dispatchToken;   var  _messages  =  {};   ! class  FooStore  extends  EventEmitter  {   !    constructor()  {          super();          _dispatchToken  =  AppDispatcher.register((action)  =>  {   !            switch(action.type)  {   !                case  ActionTypes.MESSAGE_CREATE:                      var  message  =  {                          id:  Date.now(),                          text:  action.text                      }                      _messages[message.id]  =  message;                      this.emit('change');                      break;   !                case  ActionTypes.MESSAGE_DELETE:                      delete  _messages[action.messageID];                      this.emit('change');                      break;   !                default:                      //  no  op              }   !        });      }   !    getDispatchToken()  {          return  _dispatchToken;      }   !    getMessages()  {          return  _messages;      }   ! }   ! module.exports  =  new  FooStore();          _dispatchToken  =  AppDispatcher.register((action)  =>  {   !            switch(action.type)  {   !                case  ActionTypes.MESSAGE_CREATE:                      var  message  =  {                          id:  Date.now(),                          text:  action.text                      }                      _messages[message.id]  =  message;                      this.emit('change');                      break;   !                case  ActionTypes.MESSAGE_DELETE:                      delete  _messages[action.messageID];                      this.emit('change');                      break;   !                default:                      //  no  op              }   !        });

Slide 43

Slide 43 text

       _dispatchToken  =  AppDispatcher.register((action)  =>  {   !            switch(action.type)  {   !                case  ActionTypes.MESSAGE_CREATE:                      var  message  =  {                          id:  Date.now(),                          text:  action.text                      }                      _messages[message.id]  =  message;                      this.emit('change');                      break;   !                case  ActionTypes.MESSAGE_DELETE:                      delete  _messages[action.messageID];                      this.emit('change');                      break;   !                default:                      //  no  op              }   !        });

Slide 44

Slide 44 text

TESTING STORES WITH JEST Stores have no setters — how to test is not obvious Jest is Facebook’s auto-mocking test framework Built on top of Jasmine

Slide 45

Slide 45 text

callback  =  AppDispatcher.register.mock.calls[0][0];  

Slide 46

Slide 46 text

jest.dontMock('FooStore');   ! var  AppConstants  =  require('AppConstants');   ! var  ActionTypes  =  AppConstants.ActionTypes;   ! describe('FooStore',  function()  {   !    var  callback;   !    beforeEach(function()  {          AppDispatcher  =  require('AppDispatcher');          FooStore  =  require('FooStore');          callback  =  AppDispatcher.register.mock.calls[0][0];      });   !    it('can  create  messages',  function()  {          callback({              type:  ActionTypes.MESSAGE_CREATE,              text:  'test'          });          var  messages  =  FooStore.getMessages();          var  firstKey  =  Objects.keys(messages)[0];          expect(FooStore.getMessages()[firstKey].text).toBe(‘test’);      });   ! }); callback  =  AppDispatcher.register.mock.calls[0][0];  

Slide 47

Slide 47 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  FooActionCreators.js AppDispatcher.js stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js views   ├──  FooControllerView.react.js   └──  FooChild.react.js Dispatcher Action Store View

Slide 48

Slide 48 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  FooActionCreators.js AppDispatcher.js stores   ├──  FooStore.js   └──  __tests__          └──  FooStore-­‐test.js views   ├──  FooControllerView.react.js   └──  FooChild.react.js Dispatcher Action Store View

Slide 49

Slide 49 text

View views   ├──  FooControllerView.react.js   └──  FooChild.react.js VIEWS & CONTROLLER VIEWS

Slide 50

Slide 50 text

VIEWS & CONTROLLER VIEWS Tree of React components Controller views are near the root, listen for change events On change, controller views query stores for new data With new data, they re-render themselves & children

Slide 51

Slide 51 text

REACT

Slide 52

Slide 52 text

REACT AND THE DOM Component-based system for managing DOM updates Uses a “Virtual DOM”: data structure and algorithm Updates the DOM as efficiently as possible Huge performance boost Bonus: we can stop thinking about managing the DOM

Slide 53

Slide 53 text

REACT’S PARADIGM Based on Functional-Reactive principles Unidirectional data flow Composability Predictable, Reliable, Testable Declarative: what the UI should look like, given props

Slide 54

Slide 54 text

USING REACT Data is provided through props Rendering is a function of this.props and this.state “Re-render” (or not) on every state change Keep components as stateless as possible Component lifecycle and update cycle methods

Slide 55

Slide 55 text

getDefaultProps()   getInitialState()   componentWillMount()   render() componentWillReceiveProps()*   shouldComponentUpdate()   componentWillUpdate()   render() DOM  Mutations  Complete componentDidMount() componentDidUpdate() componentWillUnmount() Lifecycle:     Mounting  and  Unmounting Update:     New  Props  or  State *Called  only  with  new  props

Slide 56

Slide 56 text

getDefaultProps()   getInitialState()   componentWillMount()   render() componentWillReceiveProps()*   shouldComponentUpdate()   componentWillUpdate()   render() DOM  Mutations  Complete componentDidMount() componentDidUpdate() componentWillUnmount() Lifecycle:     Mounting  and  Unmounting Update:     New  Props  or  State *Called  only  with  new  props

Slide 57

Slide 57 text

! ! var  React  =  require('react');   ! ! ! ! ! ! ! ! ! module.exports  =  FooControllerView;   //  FooControllerView.react.js   var  FooControllerView  =  React.createClass({   !    render:  function()  {          //  TODO      }   ! });

Slide 58

Slide 58 text

//  FooControllerView.react.js   var  FooControllerView  =  React.createClass({   !    render:  function()  {          //  TODO      }   ! });

Slide 59

Slide 59 text

//  FooControllerView.react.js   var  FooControllerView  =  React.createClass({   !    componentDidMount:  function()  {          FooStore.on('change',  this._onChange);      },   !    componentWillUnmount:  function()  {          FooStore.removeListener('change',  this._onChange);      },   !    render:  function()  {          //  TODO      }   ! });

Slide 60

Slide 60 text

//  FooControllerView.react.js   var  FooControllerView  =  React.createClass({   !    getInitialState:  function()  {          return  {  messages:  FooStore.getMessages()  };      },   !    componentDidMount:  function()  {          FooStore.on('change',  this._onChange);      },   !    componentWillUnmount:  function()  {          FooStore.removeListener('change',  this._onChange);      },   !    render:  function()  {          //  TODO      }   !    _onChange:  function()  {          this.setState({  messages:  FooStore.getMessages()  });      },   ! });

Slide 61

Slide 61 text

var  FooControllerView  =  React.createClass({   !    getInitialState:  function()  {          return  {  messages:  FooStore.getMessages()  };      },   !    componentDidMount:  function()  {          FooStore.on('change',  this._onChange);      },   !    componentWillUnmount:  function()  {          FooStore.removeListener('change',  this._onChange);      },   !                   !    _onChange:  function()  {          this.setState({  messages:  FooStore.getMessages()  });      },   ! }); //  FooControllerView.react.js      render:  function()  {          //  TODO      }

Slide 62

Slide 62 text

   render:  function()  {          //  TODO      }

Slide 63

Slide 63 text

render:  function()  {      return  (          
             
     );   },

Slide 64

Slide 64 text

render:  function()  {      var  messageListItems  =  [];      return  (          
                 {messageListItems}          
     );   },

Slide 65

Slide 65 text

render:  function()  {      var  messageListItems  =  this.state.messages.map(message  =>  {          return  (                        );      });      return  (          
                 {messageListItems}          
     );   },

Slide 66

Slide 66 text

//  FooChild.react.js   ! var  React  =  require('react');   ! var  FooChild  =  React.createClass({   !    propTypes  =  {          id:  React.PropTypes.number.isRequired,          text:  React.PropTypes.string.isRequired      }   !    render:  function()  {          return  (              
  • {this.props.text}
  •          );      }   ! });   ! module.exports  =  FooChild;  

    Slide 67

    Slide 67 text

    INITIALIZATION OF THE APP Usually done in a bootstrap module Initializes stores with an action Renders the topmost React component

    Slide 68

    Slide 68 text

    CALLING A WEB API Use a WebAPIUtils module to encapsulate XHR work. Start requests directly in the Action Creators, or in the stores. Important: create a new action on success/error. Data must enter the system through an action.

    Slide 69

    Slide 69 text

    FLUX Dataflow Programming CQRS Functional and FRP MVC Reactive WHERE DOES FLUX FIT IN

    Slide 70

    Slide 70 text

    FLUX TOTALLY WORKS Dispatcher Action Store View

    Slide 71

    Slide 71 text

    THANK YOU!