Slide 1

Slide 1 text

React + Flux Two Great Tastes that Taste Great Together Bill Fisher @fisherwebdev #ReactJS

Slide 2

Slide 2 text

https://speakerdeck.com/fisherwebdev/react-flux-fluent-2015 bit.ly/1Hrqfpc

Slide 3

Slide 3 text

React https://speakerdeck.com/fisherwebdev/react-flux-fluent-2015 Flux +

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Model Model Model Model

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

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

"Tangled wires, Freegeek, Portland, Oregon, USA" by Cory Doctorow, used under CC BY 2.0\

Slide 15

Slide 15 text

"Spaghetti? Yum!" by Dan McKay, used under CC BY 2.0

Slide 16

Slide 16 text

Dispatcher Action Store View

Slide 17

Slide 17 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  MessageActionCreators.js AppDispatcher.js stores   ├──  MessageStore.js   └──  __tests__          └──  MessageStore-­‐test.js views   ├──  MessageControllerView.react.js   └──  MessageListItem.react.js Dispatcher Action Store View

Slide 18

Slide 18 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  MessageActionCreators.js AppDispatcher.js stores   ├──  MessageStore.js   └──  __tests__          └──  MessageStore-­‐test.js views   ├──  MessageControllerView.react.js   └──  MessageListItem.react.js Dispatcher Action Store View

Slide 19

Slide 19 text

actions   └──  MessageActionCreators.js Action Actions & Action Creators

Slide 20

Slide 20 text

Actions & Action Creators Actions: an object with a type property and new data, like events Action creators: semantic methods that create actions collected together in a module to become an API

Slide 21

Slide 21 text

"A copy of the San Francisco Chronicle from when the Apollo 11 mission made it to the moon." by zpeckler, used under CC BY 2.0

Slide 22

Slide 22 text

//  MessageActionCreators.js   ! var  AppDispatcher  =  require('../AppDispatcher');   var  AppConstants  =  require('../AppConstants');   ! var  ActionTypes  =  Constants.ActionTypes;   ! module.exports  =  {   !     ! ! ! ! ! ! }; messageCreated:  text  =>  {      AppDispatcher.dispatch({          type:  ActionTypes.MESSAGE_CREATED,          text      });   },

Slide 23

Slide 23 text

messageCreated:  text  =>  {      AppDispatcher.dispatch({          type:  ActionTypes.MESSAGE_CREATED,          text      });   },

Slide 24

Slide 24 text

   messageCreated:  text  =>  {          AppDispatcher.dispatch({              type:  ActionTypes.MESSAGE_CREATED,              text          });      }

Slide 25

Slide 25 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  MessageActionCreators.js AppDispatcher.js stores   ├──  MessageStore.js   └──  __tests__          └──  MessageStore-­‐test.js views   ├──  MessageControllerView.react.js   └──  MessageListItem.react.js Dispatcher Action Store View

Slide 26

Slide 26 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! AppDispatcher.js Dispatcher Action Store View stores   ├──  MessageStore.js   └──  __tests__          └──  MessageStore-­‐test.js views   ├──  MessageControllerView.react.js   └──  MessageListItem.react.js actions   └──  MessageActionCreators.js

Slide 27

Slide 27 text

AppDispatcher.js Dispatcher The Dispatcher

Slide 28

Slide 28 text

The Dispatcher "Sunset at Sutro" by ohad*, used under CC BY 2.0

Slide 29

Slide 29 text

Dispatcher Action Store View

Slide 30

Slide 30 text

Available through npm or Bower as flux A singleton registry of callbacks, similar to EventEmitter dispatch(action): invokes all callbacks, provides action Primary API: dispatch(), register(), waitFor() The Dispatcher

Slide 31

Slide 31 text

waitFor() is a distinctive feature, vital for derived state Cannot dispatch within a dispatch Each dispatch is a discrete moment of change The Dispatcher

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  MessageActionCreators.js AppDispatcher.js stores   ├──  MessageStore.js   └──  __tests__          └──  MessageStore-­‐test.js views   ├──  MessageControllerView.react.js   └──  MessageListItem.react.js Dispatcher Action Store View

Slide 34

Slide 34 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! AppDispatcher.js stores   ├──  MessageStore.js   └──  __tests__          └──  MessageStore-­‐test.js Dispatcher Action Store View actions   └──  MessageActionCreators.js views   ├──  MessageControllerView.react.js   └──  MessageListItem.react.js

Slide 35

Slide 35 text

Stores Store stores   ├──  MessageStore.js   └──  __tests__          └──  MessageStore-­‐test.js

Slide 36

Slide 36 text

Stores Courtesey of The Bancroft Library, UC Berkeley BANC PIC 1905.17500 v.10:141--ALB

Slide 37

Slide 37 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 38

Slide 38 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 39

Slide 39 text

//  MessageStore.js   ! var  _dispatchToken;   var  _messages  =  {};   ! class  MessageStore  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  MessageStore();          _dispatchToken  =  AppDispatcher.register(action  =>  {   !            switch(action.type)  {   !                case  ActionTypes.MESSAGE_CREATED:                      var  message  =  {                          id:  Date.now(),                          text:  action.text                      }                      _messages[message.id]  =  message;                      this.emit('change');                      break;   !                case  ActionTypes.MESSAGE_DELETED:                      delete  _messages[action.messageID];                      this.emit('change');                      break;   !                default:                      //  no  op              }   !        });  

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Dispatcher Action Store View js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! actions   └──  MessageActionCreators.js AppDispatcher.js stores   ├──  MessageStore.js   └──  __tests__          └──  MessageStore-­‐test.js views   ├──  MessageControllerView.react.js   └──  MessageListItem.react.js

Slide 42

Slide 42 text

js   ├──   │   ├──  AppBootstrap.js   ├──  AppConstants.js   ├──   ├──   │   │   │   ├──  utils   │      ├──  AppWebAPIUtils.js   │      ├──  FooUtils.js   │      └──  __tests__   │              ├──  AppWebAPIUtils-­‐test.js   │              └──  FooUtils-­‐test.js   └──     ! AppDispatcher.js views   ├──  MessageControllerView.react.js   └──  MessageListItem.react.js Dispatcher Action Store View actions   └──  MessageActionCreators.js stores   ├──  MessageStore.js   └──  __tests__          └──  MessageStore-­‐test.js

Slide 43

Slide 43 text

Views & “Controller” Views View views   ├──  MessageControllerView.react.js   └──  MessageListItem.react.js

Slide 44

Slide 44 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 45

Slide 45 text

No content

Slide 46

Slide 46 text

React & the DOM Component-based framework for managing DOM updates Uses a “Virtual DOM”: data structure and diff algorithm Updates the DOM as efficiently as possible Huge performance boost Bonus: we can stop thinking about managing the DOM

Slide 47

Slide 47 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 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

    Slide 53

    Slide 53 text

    Dispatcher Action Store View messageDeleted:  messageID  =>  {      AppDispatcher.dispatch({          type:  ActionTypes.MESSAGE_DELETED,          messageID      });   } case  ActionTypes.MESSAGE_DELETED:      delete  _messages[action.messageID];      this.emit('change');      break;   _onChange:  function()  {      this.setState({            messages:  MessageStore.getMessages()        });   }

    Slide 54

    Slide 54 text

    Initialization of the App Usually done in a bootstrap module Initializes stores with an action Renders the topmost React component

    Slide 55

    Slide 55 text

    //  AppBootstrap.js   ! var  AppConstants  =  require('AppConstants');   var  AppDispatcher  =  require('AppDispatcher');   var  AppRoot  =  require('AppRoot.react');   var  React  =  require('React');   ! require('FriendStore');   require('LoggingStore');   require('MessageStore');   ! module.exports  =  (initialData,  elem)  =>  {      AppDispatcher.dispatch({          type:  AppConstants.ActionTypes.INITIALIZE,          initialData      });      React.render(,  elem);   };

    Slide 56

    Slide 56 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 57

    Slide 57 text

    Immutable Data Boost performance in React’s shouldComponentUpdate() React.addons.PureRenderMixin immutable-js: http://facebook.github.io/immutable-js/

    Slide 58

    Slide 58 text

    More Flux Patterns LoggingStore Error handling with client ID / dirty bit Error handling with actions queue Resolving dependencies in the Controller-view

    Slide 59

    Slide 59 text

    Anti-patterns Application state/logic in React components Getters in render() Public setters in stores & the setter mindset trap Props in getInitialState()

    Slide 60

    Slide 60 text

    Flux Dataflow Programming CQRS Functional and FRP MVC Reactive

    Slide 61

    Slide 61 text

    FLUX TOTALLY WORKS Dispatcher Action Store View

    Slide 62

    Slide 62 text

    Thank You! https://speakerdeck.com/fisherwebdev/react-flux-fluent

    Slide 63

    Slide 63 text

    Thank You! https://speakerdeck.com/fisherwebdev/react-flux-fluent-2015 http://facebook.github.io/immutable-js/ http://facebook.github.io/react/ http://facebook.github.io/jest/ http://facebook.github.io/flux/