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 + React is a framework for building UI, for the view layer. Flux is a pattern, not a framework, for managing application state. ! There’s a lot of excitement about React these days. Lots of people are using React and Flux - Khan Academy, Yahoo, Airbnb, Soundcloud, and many others. ! React and Flux were created separately, but they compliment each other as each is based on similar ideas: a unidirectional data flow continuous change declarative code over imperative code simplicity !

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 A story…

Slide 6

Slide 6 text

Model Model Model Model The perfect application, with no derived state.

Slide 7

Slide 7 text

Model Model Model Model Eventually, we need a feature that requires derived state. We need to know the state of one model within another model, so that model will know if or how it should update itself.

Slide 8

Slide 8 text

Model Model Model Model The model with derived state is now a second class citizen in the app.

Slide 9

Slide 9 text

Model Model Model Model Usually, these data dependencies are needed by many models.

Slide 10

Slide 10 text

Model Model Model Model Additional features will create new dependency trees. Now we have a weave of dependencies that we need to maintain, reason about, and be able to explain to new members of our team.

Slide 11

Slide 11 text

And the application grows.

Slide 12

Slide 12 text

And grows some more.

Slide 13

Slide 13 text

And eventually, we might have no single source of truth in the application as our application state has become a tangled weave of data dependencies. It’s very difficult to reason about an application like this. As changes cascade through the data layer, our application state becomes highly unstable.

Slide 14

Slide 14 text

"Tangled wires, Freegeek, Portland, Oregon, USA" by Cory Doctorow, used under CC BY 2.0\ What we have is complete mess. !

Slide 15

Slide 15 text

"Spaghetti? Yum!" by Dan McKay, used under CC BY 2.0 It’s a form of spaghetti code. ! Many people deal with this with an evented, publish-subscribe approach, but this just turns our spaghetti code into invisible spaghetti code, making it even more unstable as we have no control over the order in which the events will propagate changes through the application. ! So at Facebook, we took a look at this problem, and instead of going down a traditional MVC path, we created Flux.

Slide 16

Slide 16 text

Dispatcher Action Store View This is Flux: - avoid spaghetti code - grow apps with confidence - bring on new people quickly - mental models matter! Four major parts: action, dispatcher, store, views (React)

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 Actions are a lot like events, in that they have a type and they carry some data along with them. ! Actions creators are just methods that hand actions to the dispatcher. ! We collect them together to make them easy to find at a later time, and this library becomes the 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 Actions should be like newspapers, reporting some something that has happened in the world. For example, the user clicked, the server responded, or the browser completed an animation frame.

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      });   }, Here’s what they look like inside the action creators module. Let’s focus on a single action creator.

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          });      } Here we can see the action itself in yellow. The action creator is simply handing that yellow action object to the dispatcher. ! Note that I’m using the past tense to name both the method and the type. ! I could have named this “createMessage” but instead I’ve named it “messageCreated”. ! I’m doing that to keep myself focused on the idea that this is a report on something that has already happened, not an imperative command involving implementation details. !

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 The dispatcher is like a radio tower, indiscriminately broadcasting the action to all of the stores.

Slide 29

Slide 29 text

Dispatcher Action Store View It’s also like the the center of a dipole. All data flows through the dispatcher. ! Diagrams like this, showing magnetic flux, were the mental image in Jing Chen’s mind when she named Flux.

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 Stores are highly decoupled domain models and are the locus of control in the application.

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 That is, they manage the state for a logical domain: we might have a FriendStore, a PhotoStore or a MessageStore where a collection is managed, or we might have something more abstract like a TimeStore. ! Collections or values may be managed - any way to manage state that makes sense.

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 How they work: 1. Register a callback with dispatcher - this is the only way into the store. 2. When state changes, they emit change to alert the view layer. ! If data dependencies occur, we use waitFor()

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              }   !        });   private variables at the top getters at the bottom the real action is in the registered callback

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              }   !        });   respond to action types, or do nothing if this store is not interested emit change after modifying private variables that hold application state

Slide 41

Slide 41 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 http://facebook.github.io/jest/ http://facebook.github.io/flux/docs/testing-flux- applications.html

Slide 42

Slide 42 text

callback  =  AppDispatcher.register.mock.calls[0][0];   Give me a reference to the first argument of the first call to AppDispatcher's register() method.

Slide 43

Slide 43 text

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

Slide 44

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

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

Slide 46 text

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

Slide 47

Slide 47 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 View in a Flux application are a tree of React components. Near the top of that tree, we have some special views that we call “controller” views - that’s a somewhat controversial name - they aren’t really controllers. Some people use the name containers, but I think a better name might be “querying views”. The thing that differentiates them is that they query the stores that have changed and provide that data to their children, and the data flows down the tree.

Slide 48

Slide 48 text

Now let’s stop for a second and look at React.

Slide 49

Slide 49 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 Some people describe React as a component based framework for managing DOM updates.

Slide 50

Slide 50 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 React is more than that, however. The real innovation with React is its programming paradigm. The Virtual DOM is merely an implementation detail that helps to provide us with that paradigm. The really great thing about React is that it’s based on functional-reactive principles. ! We can think of each function in React as being like a pure function in functional programming. And just like pure functions, we can compose them together, with the data output of each function leading into the next, in a unidirectional data flow. ! And like pure functions, React components are very predictable, reliable and testable. ! We can declare what our UI should look like, given any props.

Slide 51

Slide 51 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 Props are like the arguments of the component. They are how we provide data to the component. ! And in fact rendering is a function of this.props and this.state. Props come from the outside, but state in an internal map of values. ! But if we’re smart about it, we’ll avoid using this.state and we’ll keep our components as stateless as possible. This keeps our components pure, like pure functions: that is, we will always get the same result, given specific values for props. ! We can also freely re-render at will — this complements Flux very nicely, as we can immediately see any changes in our application state reflected in our UI. ! React also provides us with hooks into each component’s lifecycle and update cycles.

Slide 52

Slide 52 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   Here is a component where I’m going to use four lifecycle methods to make it a controller view.

Slide 53

Slide 53 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   Here they are in yellow. I’m just using these methods to listen to the store, and to set up some initial state by pulling data from the store. ! I’ll also create an onChange handler so that I can update that state. Whenever I call setState to update a React component’s state it will re-render itself and all of its children.

Slide 54

Slide 54 text

render:  function()  {      var  messageListItems  =  this.state.messages.map(message  =>  {          return  (                        );      });      return  (          
                 {messageListItems}          
     );   }, Now in the render method, we can map over the messages we’ve stored in this.state and create a child component — a list item — out of each one. ! — ! provide unique key for arrays of children

Slide 55

Slide 55 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; Here’s our list item. It’s going to raise an error if we haven’t passed in the correct data as props. We could lift the hard requirement and show a spinner instead. onClick handler will be automatically handled by event delegation In that handler, we’ll call an action creator to express what the user has done.

    Slide 56

    Slide 56 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()        });   } Now we’ve come full circle. ! First our action will get handed to the dispatcher by the action creator. Then our store will handle that action and emit change. Then our controller view will respond to the change event by querying for new data, and update itself and all of its children.

    Slide 57

    Slide 57 text

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

    Slide 58

    Slide 58 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 59

    Slide 59 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 60

    Slide 60 text

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

    Slide 61

    Slide 61 text

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

    Slide 62

    Slide 62 text

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

    Slide 63

    Slide 63 text

    Flux Dataflow Programming CQRS Functional and FRP MVC Reactive

    Slide 64

    Slide 64 text

    FLUX TOTALLY WORKS Dispatcher Action Store View

    Slide 65

    Slide 65 text

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

    Slide 66

    Slide 66 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/