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 !
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.
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.
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.
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.
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.
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. !
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.
of callbacks, similar to EventEmitter dispatch(action): invokes all callbacks, provides action Primary API: dispatch(), register(), waitFor() The Dispatcher
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.
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()
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
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
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.
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.
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.
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.
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.
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.
{ return ( <MessageListItem key={message.id} messageID={message.id} text={message.text} /> ); }); return ( <ul> {messageListItems} </ul> ); }, 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
React = require('react'); ! var MessageListItem = React.createClass({ ! propTypes = { messageID: React.PropTypes.number.isRequired, text: React.PropTypes.string.isRequired }, ! render: function() { return ( <li onClick={this._onClick}> {this.props.text} </li> ); }, ! _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.
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.
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.