@ -14,4 +14,4 @@ In Flux, the Dispatcher is a singleton that directs the flow of data and ensures
When a user interacts with a React view, the view sends an action (usually represented as a JavaScript object with some fields) through the dispatcher, which notifies the various stores that hold the application's data and business logic. When the stores change state, they notify the views that something has updated. This works especially well with React's declarative model, which allows the stores to send updates without specifying how to transition views between states.
When a user interacts with a React view, the view sends an action (usually represented as a JavaScript object with some fields) through the dispatcher, which notifies the various stores that hold the application's data and business logic. When the stores change state, they notify the views that something has updated. This works especially well with React's declarative model, which allows the stores to send updates without specifying how to transition views between states.
Flux is more of a pattern than a formal framework, so you can start using Flux immediately without a lot of new code. An [example of this architecture](https://github.com/facebook/react/tree/master/examples/todomvc-flux) is available, along with more [detailed documentation](http://facebook.github.io/react/docs/flux-overview.html) and a [tutorial](http://facebook.github.io/react/docs/flux-todo-list.html). Look for more examples to come in the future.
Flux is more of a pattern than a formal framework, so you can start using Flux immediately without a lot of new code. An [example of this architecture](https://github.com/facebook/flux/tree/master/examples/flux-todomvc) is available, along with more [detailed documentation](http://facebook.github.io/react/docs/flux-overview.html) and a [tutorial](http://facebook.github.io/react/docs/flux-todo-list.html). Look for more examples to come in the future.
A unidirectional data flow is central to the Flux pattern, and in fact Flux takes its name from the Latin word for flow. In the above diagram, the dispatcher, stores and views are independent nodes with distinct inputs and outputs. The actions are simply discrete, semantic helper functions that facilitate passing data to the dispatcher.
A unidirectional data flow is central to the Flux pattern, and in fact Flux takes its name from the Latin word for flow. In the above diagram, the dispatcher, stores and views are independent nodes with distinct inputs and outputs. The actions are simply discrete, semantic helper functions that facilitate passing data to the dispatcher.
All data flows through the dispatcher as a central hub. Actions most often originate from user interactions with the views, and are nothing more than a call into the dispatcher. The dispatcher then invokes the callbacks that the stores have registered with it, effectively dispatching the data payload contained in the actions to all stores. Within their registered callbacks, stores determine which actions they are interested in, and respond accordingly. The stores then emit a "change" event to alert the controller-views that a change to the data layer has occurred. Controller-views listen for these events and retrieve data from the stores in an event handler. The controller-views call their own `render()` method via `setState()` or `forceUpdate()`, updating themselves and all of their children.
All data flows through the dispatcher as a central hub. Actions most often originate from user interactions with the views, and are nothing more than a call into the dispatcher. The dispatcher then invokes the callbacks that the stores have registered with it, effectively dispatching the data payload contained in the actions to all stores. Within their registered callbacks, stores determine which actions they are interested in, and respond accordingly. The stores then emit a "change" event to alert the controller-views that a change to the data layer has occurred. Controller-views listen for these events and retrieve data from the stores in an event handler. The controller-views call their own `render()` method via `setState()` or `forceUpdate()`, updating themselves and all of their children.
This structure allows us to reason easily about our application in a way that is reminiscent of functional reactive programming, or more specifically data-flow programming or flow-based programming, where data flows through the application in a single direction — there are no two-way bindings. Application state is maintained only in the stores, allowing the different parts of the application to remain highly decoupled. Where dependencies do occur between stores, they are kept in a strict hierarchy, with synchronous updates managed by the dispatcher.
This structure allows us to reason easily about our application in a way that is reminiscent of functional reactive programming, or more specifically data-flow programming or flow-based programming, where data flows through the application in a single direction — there are no two-way bindings. Application state is maintained only in the stores, allowing the different parts of the application to remain highly decoupled. Where dependencies do occur between stores, they are kept in a strict hierarchy, with synchronous updates managed by the dispatcher.
We found that two-way data bindings led to cascading updates, where changing one object led to another object changing, which could also trigger more updates. As applications grew, these cascading updates made it very difficult to predict what would change as the result of one user interaction. When updates can only change data within a single round, the system as a whole becomes more predictable.
We found that two-way data bindings led to cascading updates, where changing one object led to another object changing, which could also trigger more updates. As applications grew, these cascading updates made it very difficult to predict what would change as the result of one user interaction. When updates can only change data within a single round, the system as a whole becomes more predictable.
Let's look at the various parts of the Flux update cycle up close. A good place to start is the dispatcher.
Let's look at the various parts of the Flux update cycle up close. A good place to start is the dispatcher.
### A Single Dispatcher
### A Single Dispatcher
The dispatcher is the central hub that manages all data flow in a Flux application. It is essentially a registry of callbacks into the stores. Each store registers itself and provides a callback. When the dispatcher responds to an action, all stores in the application are sent the data payload provided by the action via the callbacks in the registry.
The dispatcher is the central hub that manages all data flow in a Flux application. It is essentially a registry of callbacks into the stores. Each store registers itself and provides a callback. When the dispatcher responds to an action, all stores in the application are sent the data payload provided by the action via the callbacks in the registry.
As an application grows, the dispatcher becomes more vital, as it can manage dependencies between stores by invoking the registered callbacks in a specific order. Stores can declaratively wait for other stores to finish updating, and then update themselves accordingly.
As an application grows, the dispatcher becomes more vital, as it can manage dependencies between stores by invoking the registered callbacks in a specific order. Stores can declaratively wait for other stores to finish updating, and then update themselves accordingly.
### Stores
### Stores
Stores contain the application state and logic. Their role is somewhat similar to a model in a traditional MVC, but they manage the state of many objects — they are not instances of one object. Nor are they the same as Backbone's collections. More than simply managing a collection of ORM-style objects, stores manage the application state for a particular __domain__ within the application.
Stores contain the application state and logic. Their role is somewhat similar to a model in a traditional MVC, but they manage the state of many objects — they are not instances of one object. Nor are they the same as Backbone's collections. More than simply managing a collection of ORM-style objects, stores manage the application state for a particular __domain__ within the application.
For example, Facebook's [Lookback Video Editor](https://facebook.com/lookback/edit) utilized a TimeStore that kept track of the playback time position and the playback state. On the other hand, the same application's ImageStore kept track of a collection of images. The TodoStore in our [TodoMVC example](https://github.com/facebook/react/tree/master/examples/todomvc-flux) is similar in that it manages a collection of to-do items. A store exhibits characteristics of both a collection of models and a singleton model of a logical domain.
For example, Facebook's [Lookback Video Editor](https://facebook.com/lookback/edit) utilized a TimeStore that kept track of the playback time position and the playback state. On the other hand, the same application's ImageStore kept track of a collection of images. The TodoStore in our [TodoMVC example](https://github.com/facebook/flux/tree/master/examples/flux-todomvc) is similar in that it manages a collection of to-do items. A store exhibits characteristics of both a collection of models and a singleton model of a logical domain.
As mentioned above, a store registers itself with the dispatcher and provides it with a callback. This callback receives the action's data payload as a parameter. The payload contains a type attribute, identifying the action's type. Within the store's registered callback, a switch statement based on the action's type is used to interpret the payload and to provide the proper hooks into the store's internal methods. This allows an action to result in an update to the state of the store, via the dispatcher. After the stores are updated, they broadcast an event declaring that their state has changed, so the views may query the new state and update themselves.
As mentioned above, a store registers itself with the dispatcher and provides it with a callback. This callback receives the action's data payload as a parameter. The payload contains a type attribute, identifying the action's type. Within the store's registered callback, a switch statement based on the action's type is used to interpret the payload and to provide the proper hooks into the store's internal methods. This allows an action to result in an update to the state of the store, via the dispatcher. After the stores are updated, they broadcast an event declaring that their state has changed, so the views may query the new state and update themselves.
### Views and Controller-Views
### Views and Controller-Views
React provides the kind of composable views we need for the view layer. Close to the top of the nested view hierarchy, a special kind of view listens for events that are broadcast by the stores that it depends on. One could call this a controller-view, as it provides the glue code to get the data from the stores and to pass this data down the chain of its descendants. We might have one of these controller-views governing any significant section of the page.
React provides the kind of composable views we need for the view layer. Close to the top of the nested view hierarchy, a special kind of view listens for events that are broadcast by the stores that it depends on. One could call this a controller-view, as it provides the glue code to get the data from the stores and to pass this data down the chain of its descendants. We might have one of these controller-views governing any significant section of the page.
When it receives the event from the store, it first requests the new data it needs via the stores' public getter methods. It then calls its own `setState()` or `forceUpdate()` methods, causing its `render()` method and the `render()` method of all its descendants to run.
When it receives the event from the store, it first requests the new data it needs via the stores' public getter methods. It then calls its own `setState()` or `forceUpdate()` methods, causing its `render()` method and the `render()` method of all its descendants to run.
We often pass the entire state of the store down the chain of views in a single object, allowing different descendants to use what they need. In addition to keeping the controller-like behavior at the top of the hierarchy, and thus keeping our descendant views as functionally pure as possible, passing down the entire state of the store in a single object also has the effect of reducing the number of props we need to manage.
We often pass the entire state of the store down the chain of views in a single object, allowing different descendants to use what they need. In addition to keeping the controller-like behavior at the top of the hierarchy, and thus keeping our descendant views as functionally pure as possible, passing down the entire state of the store in a single object also has the effect of reducing the number of props we need to manage.
Occasionally we may need to add additional controller-views deeper in the hierarchy to keep components simple. This might help us to better encapsulate a section of the hierarchy related to a specific data domain. Be aware, however, that controller-views deeper in the hierarchy can violate the singular flow of data by introducing a new, potentially conflicting entry point for the data flow. In making the decision of whether to add a deep controller-view, balance the gain of simpler components against the complexity of multiple data updates flowing into the hierarchy at different points. These multiple data updates can lead to odd effects, with React's render method getting invoked repeatedly by updates from different controller-views, potentially increasing the difficulty of debugging.
Occasionally we may need to add additional controller-views deeper in the hierarchy to keep components simple. This might help us to better encapsulate a section of the hierarchy related to a specific data domain. Be aware, however, that controller-views deeper in the hierarchy can violate the singular flow of data by introducing a new, potentially conflicting entry point for the data flow. In making the decision of whether to add a deep controller-view, balance the gain of simpler components against the complexity of multiple data updates flowing into the hierarchy at different points. These multiple data updates can lead to odd effects, with React's render method getting invoked repeatedly by updates from different controller-views, potentially increasing the difficulty of debugging.
### Actions
### Actions
The dispatcher exposes a method that allows a view to trigger a dispatch to the stores, and to include a payload of data, or an action. The action construction may be wrapped into a semantic helper method which sends the payload to the dispatcher. For example, we may want to change the text of a to-do item in a to-do list application. We would create an action with a function signature like `updateText(todoId, newText)` in our `TodoActions` module. This method may be invoked from within our views' event handlers, so we can call it in response to a user action. This action method also adds the action type to the payload, so that when the payload is interpreted in the store, it can respond appropriately to a payload with a particular action type. In our example, this type might be named something like `TODO_UPDATE_TEXT`.
The dispatcher exposes a method that allows a view to trigger a dispatch to the stores, and to include a payload of data, or an action. The action construction may be wrapped into a semantic helper method which sends the payload to the dispatcher. For example, we may want to change the text of a to-do item in a to-do list application. We would create an action with a function signature like `updateText(todoId, newText)` in our `TodoActions` module. This method may be invoked from within our views' event handlers, so we can call it in response to a user action. This action method also adds the action type to the payload, so that when the payload is interpreted in the store, it can respond appropriately to a payload with a particular action type. In our example, this type might be named something like `TODO_UPDATE_TEXT`.
Actions may also come from other places, such as the server. This happens, for example, during data initialization. It may also happen when the server returns an error code or when the server has updates to provide to the application. We'll talk more about server actions in a future article. In this post we're only concerned with the basics of the data flow.
Actions may also come from other places, such as the server. This happens, for example, during data initialization. It may also happen when the server returns an error code or when the server has updates to provide to the application. We'll talk more about server actions in a future article. In this post we're only concerned with the basics of the data flow.
### What About that Dispatcher?
### What About that Dispatcher?
As mentioned earlier, the dispatcher is also able to manage dependencies between stores. This functionality is available through the `waitFor()` method within the Dispatcher class. We did not need to use this method within the extremely simple [TodoMVC application](https://github.com/facebook/react/tree/master/examples/todomvc-flux), but we have included it [in the example dispatcher](https://github.com/facebook/react/blob/master/examples/todomvc-flux/js/dispatcher/Dispatcher.js#L110) as an example of what a dispatcher should be able to do in a larger, more complex application.
As mentioned earlier, the dispatcher is also able to manage dependencies between stores. This functionality is available through the `waitFor()` method within the Dispatcher class. We did not need to use this method within the extremely simple [TodoMVC application](https://github.com/facebook/flux/tree/master/examples/flux-todomvc), but we have included it [in the example dispatcher](https://github.com/facebook/flux/blob/master/examples/flux-todomvc/js/dispatcher/Dispatcher.js#L110) as an example of what a dispatcher should be able to do in a larger, more complex application.
Within the TodoStore's registered callback we could explicitly wait for any dependencies to first update before moving forward:
Within the TodoStore's registered callback we could explicitly wait for any dependencies to first update before moving forward:
The arguments for `waitFor()` are an array of dispatcher registry indexes, and a final callback to invoke after the callbacks at the given indexes have completed. Thus the store that is invoking `waitFor()` can depend on the state of another store to inform how it should update its own state.
The arguments for `waitFor()` are an array of dispatcher registry indexes, and a final callback to invoke after the callbacks at the given indexes have completed. Thus the store that is invoking `waitFor()` can depend on the state of another store to inform how it should update its own state.
To demonstrate the Flux architecture with some example code, let's take on the classic TodoMVC application. The entire application is available in the React GitHub repo within the [todomvc-flux](https://github.com/facebook/react/tree/master/examples/todomvc-flux) example directory, but let's walk through the development of it a step at a time.
To demonstrate the Flux architecture with some example code, let's take on the classic TodoMVC application. The entire application is available in the Flux GitHub repo within the [flux-todomvc](https://github.com/facebook/flux/tree/master/examples/flux-todomvc) example directory, but let's walk through the development of it a step at a time.
To begin, we'll need some boilerplate and get up and running with a module system. Node's module system, based on CommonJS, will fit the bill very nicely and we can build off of [react-boilerplate](https://github.com/petehunt/react-boilerplate) to get up and running quickly. Assuming you have npm installed, simply clone the react-boilerplate code from GitHub, and navigate into the resulting directory in Terminal (or whatever CLI application you like). Next run the npm scripts to get up and running: `npm install`, then `npm run build`, and lastly `npm start` to continuously build using Browserify.
To begin, we'll need some boilerplate and get up and running with a module system. Node's module system, based on CommonJS, will fit the bill very nicely and we can build off of [react-boilerplate](https://github.com/petehunt/react-boilerplate) to get up and running quickly. Assuming you have npm installed, simply clone the react-boilerplate code from GitHub, and navigate into the resulting directory in Terminal (or whatever CLI application you like). Next run the npm scripts to get up and running: `npm install`, then `npm run build`, and lastly `npm start` to continuously build using Browserify.
The TodoMVC example has all this built into it as well, but if you're starting with react-boilerplate make sure you edit your package.json file to match the file structure and dependencies described in the TodoMVC example's [package.json](https://github.com/facebook/react/tree/master/examples/todomvc-flux/package.json), or else your code won't match up with the explanations below.
The TodoMVC example has all this built into it as well, but if you're starting with react-boilerplate make sure you edit your package.json file to match the file structure and dependencies described in the TodoMVC example's [package.json](https://github.com/facebook/flux/tree/master/examples/flux-todomvc/package.json), or else your code won't match up with the explanations below.
Source Code Structure
Source Code Structure
---------------------
---------------------
The resulting index.js file may be used as the entry point into our app, but we'll put most of our code in a 'js' directory. Let's let Browserify do its thing, and now we'll open a new tab in Terminal (or a GUI file browser) to look at the directory. It should look something like this:
The resulting index.js file may be used as the entry point into our app, but we'll put most of our code in a 'js' directory. Let's let Browserify do its thing, and now we'll open a new tab in Terminal (or a GUI file browser) to look at the directory. It should look something like this:
```
```
myapp
myapp
|
|
+ ...
+ ...
+ js
+ js
|
|
+ app.js
+ app.js
+ bundle.js // generated by Browserify whenever we make changes.
+ bundle.js // generated by Browserify whenever we make changes.
+ index.html
+ index.html
+ ...
+ ...
```
```
Next we'll dive into the js directory, and layout our application's primary directory structure:
Next we'll dive into the js directory, and layout our application's primary directory structure:
```
```
myapp
myapp
|
|
+ ...
+ ...
+ js
+ js
|
|
+ actions
+ actions
+ components // all React components, both views and controller-views
+ components // all React components, both views and controller-views
+ constants
+ constants
+ dispatcher
+ dispatcher
+ stores
+ stores
+ app.js
+ app.js
+ bundle.js
+ bundle.js
+ index.html
+ index.html
+ ...
+ ...
```
```
Creating a Dispatcher
Creating a Dispatcher
---------------------
---------------------
Now we are ready to create a dispatcher. Here is an naive example of a Dispatcher class, written with JavaScript promises, polyfilled with Jake Archibald's [ES6-Promises](https://github.com/jakearchibald/ES6-Promises) module.
Now we are ready to create a dispatcher. Here is an naive example of a Dispatcher class, written with JavaScript promises, polyfilled with Jake Archibald's [ES6-Promises](https://github.com/jakearchibald/ES6-Promises) module.
The public API of this basic Dispatcher consists of only two methods: register() and dispatch(). We'll use register() within our stores to register each store's callback. We'll use dispatch() within our actions to trigger the invocation of the callbacks.
module.exports = Dispatcher;
```
Now we are all set to create a dispatcher that is more specific to our app, which we'll call AppDispatcher.
The public API of this basic Dispatcher consists of only two methods: register() and dispatch(). We'll use register() within our stores to register each store's callback. We'll use dispatch() within our actions to trigger the invocation of the callbacks.
```javascript
Now we are all set to create a dispatcher that is more specific to our app, which we'll call AppDispatcher.
```javascript
var Dispatcher = require('./Dispatcher');
var Dispatcher = require('./Dispatcher');
var merge = require('react/lib/merge');
var merge = require('react/lib/merge');
@ -129,144 +129,144 @@ var AppDispatcher = merge(Dispatcher.prototype, {
});
});
module.exports = AppDispatcher;
module.exports = AppDispatcher;
```
```
Now we've created an implementation that is a bit more specific to our needs, with a helper function we can use in the actions coming from our views' event handlers. We might expand on this later to provide a separate helper for server updates, but for now this is all we need.
Creating Stores
----------------
Now we've created an implementation that is a bit more specific to our needs, with a helper function we can use in the actions coming from our views' event handlers. We might expand on this later to provide a separate helper for server updates, but for now this is all we need.
We can use Node's EventEmitter to get started with a store. We need EventEmitter to broadcast the 'change' event to our controller-views. So let's take a look at what that looks like. I've omitted some of the code for the sake of brevity, but for the full version see [TodoStore.js](https://github.com/Facebook/flux/blob/master/examples/flux-todomvc/js/stores/TodoStore.js) in the TodoMVC example code.
```javascript
var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var TodoConstants = require('../constants/TodoConstants');
var merge = require('react/lib/merge');
var CHANGE_EVENT = 'change';
Creating Stores
var _todos = {}; // collection of todo items
----------------
We can use Node's EventEmitter to get started with a store. We need EventEmitter to broadcast the 'change' event to our controller-views. So let's take a look at what that looks like. I've omitted some of the code for the sake of brevity, but for the full version see [TodoStore.js](https://github.com/Facebook/react/blob/master/examples/todomvc-flux/js/stores/TodoStore.js) in the TodoMVC example code.
/**
* Create a TODO item.
* @param {string} text The content of the TODO
*/
function create(text) {
// Using the current timestamp in place of a real id.
var id = Date.now();
_todos[id] = {
id: id,
complete: false,
text: text
};
}
```javascript
var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var TodoConstants = require('../constants/TodoConstants');
var merge = require('react/lib/merge');
var CHANGE_EVENT = 'change';
var _todos = {}; // collection of todo items
/**
/**
* Create a TODO item.
* Delete a TODO item.
* @param {string} text The content of the TODO
* @param {string} id
*/
*/
function create(text) {
function destroy(id) {
// Using the current timestamp in place of a real id.
// add more cases for other actionTypes, like TODO_UPDATE, etc.
case TodoConstants.TODO_CREATE:
text = action.text.trim();
if (text !== '') {
create(text);
TodoStore.emitChange();
}
break;
case TodoConstants.TODO_DESTROY:
destroy(action.id);
TodoStore.emitChange();
break;
// add more cases for other actionTypes, like TODO_UPDATE, etc.
}
}
return true; // No errors. Needed by promise in Dispatcher.
return true; // No errors. Needed by promise in Dispatcher.
})
})
});
});
module.exports = TodoStore;
module.exports = TodoStore;
```
```
There are a few important things to note in the above code. To start, we are maintaining a private data structure called _todos. This object contains all the individual to-do items. Because this variable lives outside the class, but within the closure of the module, it remains private — it cannot be directly changed from the outside. This helps us preserve a distinct input/output interface for the flow of data by making it impossible to update the store without using an action.
There are a few important things to note in the above code. To start, we are maintaining a private data structure called _todos. This object contains all the individual to-do items. Because this variable lives outside the class, but within the closure of the module, it remains private — it cannot be directly changed from the outside. This helps us preserve a distinct input/output interface for the flow of data by making it impossible to update the store without using an action.
Another important part is the registration of the store's callback with the dispatcher. We pass in our payload handling callback to the dispatcher and preserve the index that this store has in the dispatcher's registry. The callback function currently only handles two actionTypes, but later we can add as many as we need.
Another important part is the registration of the store's callback with the dispatcher. We pass in our payload handling callback to the dispatcher and preserve the index that this store has in the dispatcher's registry. The callback function currently only handles two actionTypes, but later we can add as many as we need.
Listening to Changes with a Controller-View
Listening to Changes with a Controller-View
-------------------------------------------
-------------------------------------------
We need a React component near the top of our component hierarchy to listen for changes in the store. In a larger app, we would have more of these listening components, perhaps one for every section of the page. In Facebook's Ads Creation Tool, we have many of these controller-like views, each governing a specific section of the UI. In the Lookback Video Editor, we only had two: one for the animated preview and one for the image selection interface. Here's one for our TodoMVC example. Again, this is slightly abbreviated, but for the full code you can take a look at the TodoMVC example's [TodoApp.react.js](https://github.com/facebook/react/blob/master/examples/todomvc-flux/js/components/TodoApp.react.js)
We need a React component near the top of our component hierarchy to listen for changes in the store. In a larger app, we would have more of these listening components, perhaps one for every section of the page. In Facebook's Ads Creation Tool, we have many of these controller-like views, each governing a specific section of the UI. In the Lookback Video Editor, we only had two: one for the animated preview and one for the image selection interface. Here's one for our TodoMVC example. Again, this is slightly abbreviated, but for the full code you can take a look at the TodoMVC example's [TodoApp.react.js](https://github.com/facebook/flux/blob/master/examples/flux-todomvc/js/components/TodoApp.react.js)
```javascript
```javascript
/** @jsx React.DOM */
/** @jsx React.DOM */
var Footer = require('./Footer.react');
var Footer = require('./Footer.react');
var Header = require('./Header.react');
var Header = require('./Header.react');
var MainSection = require('./MainSection.react');
var MainSection = require('./MainSection.react');
var React = require('react');
var React = require('react');
var TodoStore = require('../stores/TodoStore');
var TodoStore = require('../stores/TodoStore');
function getTodoState() {
function getTodoState() {
return {
return {
allTodos: TodoStore.getAll()
allTodos: TodoStore.getAll()
};
};
}
}
var TodoApp = React.createClass({
var TodoApp = React.createClass({
getInitialState: function() {
getInitialState: function() {
return getTodoState();
return getTodoState();
},
},
componentDidMount: function() {
componentDidMount: function() {
TodoStore.addChangeListener(this._onChange);
TodoStore.addChangeListener(this._onChange);
},
},
componentWillUnmount: function() {
componentWillUnmount: function() {
TodoStore.removeChangeListener(this._onChange);
TodoStore.removeChangeListener(this._onChange);
},
},
/**
/**
* @return {object}
* @return {object}
*/
*/
@ -281,189 +281,189 @@ var TodoApp = React.createClass({
<FooterallTodos={this.state.allTodos}/>
<FooterallTodos={this.state.allTodos}/>
</div>
</div>
);
);
},
},
_onChange: function() {
this.setState(getTodoState());
}
});
module.exports = TodoApp;
```
Now we're in our familiar React territory, utilizing React's lifecycle methods. We set up the initial state of this controller-view in getInitialState(), register an event listener in componentDidMount(), and then clean up after ourselves within componentWillUnmount(). We render a containing div and pass down the collection of states we got from the TodoStore.
_onChange: function() {
this.setState(getTodoState());
}
});
The Header component contains the primary text input for the application, but it does not need to know the state of the store. The MainSection and Footer do need this data, so we pass it down to them.
module.exports = TodoApp;
```
More Views
Now we're in our familiar React territory, utilizing React's lifecycle methods. We set up the initial state of this controller-view in getInitialState(), register an event listener in componentDidMount(), and then clean up after ourselves within componentWillUnmount(). We render a containing div and pass down the collection of states we got from the TodoStore.
----------
At a high level, the React component hierarchy of the app looks like this:
The Header component contains the primary text input for the application, but it does not need to know the state of the store. The MainSection and Footer do need this data, so we pass it down to them.
More Views
----------
At a high level, the React component hierarchy of the app looks like this:
```javascript
```javascript
<TodoApp>
<TodoApp>
<Header>
<Header>
<TodoTextInput/>
<TodoTextInput/>
<MainSection>
<MainSection>
<ul>
<ul>
<TodoItem/>
<TodoItem/>
</ul>
</ul>
</MainSection>
</MainSection>
</TodoApp>
</TodoApp>
```
```
If a TodoItem is in edit mode, it will also render a TodoTextInput as a child. Let's take a look at how some of these components display the data they receive as props, and how they communicate through actions with the dispatcher.
If a TodoItem is in edit mode, it will also render a TodoTextInput as a child. Let's take a look at how some of these components display the data they receive as props, and how they communicate through actions with the dispatcher.
The MainSection needs to iterate over the collection of to-do items it received from TodoApp to create the list of TodoItems. In the component's render() method, we can do that iteration like so:
The MainSection needs to iterate over the collection of to-do items it received from TodoApp to create the list of TodoItems. In the component's render() method, we can do that iteration like so:
Now each TodoItem can display its own text, and perform actions utilizing its own ID. Explaining all the different actions that a TodoItem can invoke in the TodoMVC example goes beyond the scope of this article, but let's just take a look at the action that deletes one of the to-do items. Here is an abbreviated version of the TodoItem:
Now each TodoItem can display its own text, and perform actions utilizing its own ID. Explaining all the different actions that a TodoItem can invoke in the TodoMVC example goes beyond the scope of this article, but let's just take a look at the action that deletes one of the to-do items. Here is an abbreviated version of the TodoItem:
```javascript
```javascript
/** @jsx React.DOM */
/** @jsx React.DOM */
var React = require('react');
var React = require('react');
var TodoActions = require('../actions/TodoActions');
var TodoActions = require('../actions/TodoActions');
var TodoTextInput = require('./TodoTextInput.react');
var TodoTextInput = require('./TodoTextInput.react');
With a destroy action available in our library of TodoActions, and a store ready to handle it, connecting the user's interaction with application state changes could not be simpler. We just wrap our onClick handler around the destroy action, provide it with the id, and we're done. Now the user can click the destroy button and kick off the Flux cycle to update the rest of the application.
With a destroy action available in our library of TodoActions, and a store ready to handle it, connecting the user's interaction with application state changes could not be simpler. We just wrap our onClick handler around the destroy action, provide it with the id, and we're done. Now the user can click the destroy button and kick off the Flux cycle to update the rest of the application.
Text input, on the other hand, is just a bit more complicated because we need to hang on to the state of the text input within the React component itself. Let's take a look at how TodoTextInput works.
Text input, on the other hand, is just a bit more complicated because we need to hang on to the state of the text input within the React component itself. Let's take a look at how TodoTextInput works.
As you'll see below, with every change to the input, React expects us to update the state of the component. So when we are finally ready to save the text inside the input, we will put the value held in the component's state in the action's payload. This is UI state, rather than application state, and keeping that distinction in mind is a good guide for where state should live. All application state should live in the store, while components occasionally hold on to UI state. Ideally, React components preserve as little state as possible.
As you'll see below, with every change to the input, React expects us to update the state of the component. So when we are finally ready to save the text inside the input, we will put the value held in the component's state in the action's payload. This is UI state, rather than application state, and keeping that distinction in mind is a good guide for where state should live. All application state should live in the store, while components occasionally hold on to UI state. Ideally, React components preserve as little state as possible.
Because TodoTextInput is being used in multiple places within our application, with different behaviors, we'll need to pass the onSave method in as a prop from the component's parent. This allows onSave to invoke different actions depending on where it is used.
Because TodoTextInput is being used in multiple places within our application, with different behaviors, we'll need to pass the onSave method in as a prop from the component's parent. This allows onSave to invoke different actions depending on where it is used.
```javascript
```javascript
/** @jsx React.DOM */
/** @jsx React.DOM */
var React = require('react');
var React = require('react');
var ReactPropTypes = React.PropTypes;
var ReactPropTypes = React.PropTypes;
var ENTER_KEY_CODE = 13;
var ENTER_KEY_CODE = 13;
var TodoTextInput = React.createClass({
var TodoTextInput = React.createClass({
propTypes: {
propTypes: {
className: ReactPropTypes.string,
className: ReactPropTypes.string,
id: ReactPropTypes.string,
id: ReactPropTypes.string,
placeholder: ReactPropTypes.string,
placeholder: ReactPropTypes.string,
onSave: ReactPropTypes.func.isRequired,
onSave: ReactPropTypes.func.isRequired,
value: ReactPropTypes.string
value: ReactPropTypes.string
},
},
getInitialState: function() {
getInitialState: function() {
return {
return {
value: this.props.value || ''
value: this.props.value || ''
};
};
},
},
/**
/**
* @return {object}
* @return {object}
*/
*/
render: function() /*object*/ {
render: function() /*object*/ {
return (
return (
<input
<input
className={this.props.className}
className={this.props.className}
id={this.props.id}
id={this.props.id}
placeholder={this.props.placeholder}
placeholder={this.props.placeholder}
onBlur={this._save}
onBlur={this._save}
onChange={this._onChange}
onChange={this._onChange}
onKeyDown={this._onKeyDown}
onKeyDown={this._onKeyDown}
value={this.state.value}
value={this.state.value}
autoFocus={true}
autoFocus={true}
/>
/>
);
);
},
},
/**
/**
* Invokes the callback passed in as onSave, allowing this component to be
* Invokes the callback passed in as onSave, allowing this component to be
* used in different ways.
* used in different ways.
*/
*/
_save: function() {
_save: function() {
this.props.onSave(this.state.value);
this.props.onSave(this.state.value);
this.setState({
this.setState({
value: ''
value: ''
});
});
},
},
/**
/**
* @param {object} event
* @param {object} event
*/
*/
_onChange: function(/*object*/ event) {
_onChange: function(/*object*/ event) {
this.setState({
this.setState({
value: event.target.value
value: event.target.value
});
});
},
},
/**
/**
* @param {object} event
* @param {object} event
*/
*/
_onKeyDown: function(event) {
_onKeyDown: function(event) {
if (event.keyCode === ENTER_KEY_CODE) {
if (event.keyCode === ENTER_KEY_CODE) {
this._save();
this._save();
}
}
}
}
});
});
module.exports = TodoTextInput;
module.exports = TodoTextInput;
```
```
The Header passes in the onSave method as a prop to allow the TodoTextInput to create new
The Header passes in the onSave method as a prop to allow the TodoTextInput to create new
to-do items:
to-do items:
```javascript
```javascript
/** @jsx React.DOM */
/** @jsx React.DOM */
var React = require('react');
var React = require('react');
var TodoActions = require('../actions/TodoActions');
var TodoActions = require('../actions/TodoActions');
var TodoTextInput = require('./TodoTextInput.react');
var TodoTextInput = require('./TodoTextInput.react');
var Header = React.createClass({
var Header = React.createClass({
/**
/**
* @return {object}
* @return {object}
*/
*/
@ -478,8 +478,8 @@ var Header = React.createClass({
/>
/>
</header>
</header>
);
);
},
},
/**
/**
* Event handler called within TodoTextInput.
* Event handler called within TodoTextInput.
* Defining this here allows TodoTextInput to be used in multiple places
* Defining this here allows TodoTextInput to be used in multiple places
@ -488,26 +488,26 @@ var Header = React.createClass({
*/
*/
_onSave: function(text) {
_onSave: function(text) {
TodoActions.create(text);
TodoActions.create(text);
}
}
});
module.exports = Header;
```
In a different context, such as in editing mode for an existing to-do item, we might pass an onSave callback that invokes `TodoActions.update(text)` instead.
});
module.exports = Header;
```
Creating Semantic Actions
In a different context, such as in editing mode for an existing to-do item, we might pass an onSave callback that invokes `TodoActions.update(text)` instead.
-------------------------
Here is the basic code for the two actions we used above in our views:
```javascript
Creating Semantic Actions
/**
-------------------------
* TodoActions
*/
Here is the basic code for the two actions we used above in our views:
```javascript
/**
* TodoActions
*/
var AppDispatcher = require('../dispatcher/AppDispatcher');
var AppDispatcher = require('../dispatcher/AppDispatcher');
var TodoConstants = require('../constants/TodoConstants');
var TodoConstants = require('../constants/TodoConstants');
@ -521,8 +521,8 @@ var TodoActions = {
actionType: TodoConstants.TODO_CREATE,
actionType: TodoConstants.TODO_CREATE,
text: text
text: text
});
});
},
},
/**
/**
* @param {string} id
* @param {string} id
*/
*/
@ -531,51 +531,51 @@ var TodoActions = {
actionType: TodoConstants.TODO_DESTROY,
actionType: TodoConstants.TODO_DESTROY,
id: id
id: id
});
});
},
},
};
};
module.exports = TodoActions;
module.exports = TodoActions;
```
```
As you can see, we really would not need to have the helpers AppDispatcher.handleViewAction() or TodoActions.create(). We could, in theory, call AppDispatcher.dispatch() directly and provide a payload. But as our application grows, having these helpers keeps the code clean and semantic. It's just a lot cleaner to write TodoActions.destroy(id) instead of writing a whole lot of things that our TodoItem shouldn't have to know about.
As you can see, we really would not need to have the helpers AppDispatcher.handleViewAction() or TodoActions.create(). We could, in theory, call AppDispatcher.dispatch() directly and provide a payload. But as our application grows, having these helpers keeps the code clean and semantic. It's just a lot cleaner to write TodoActions.destroy(id) instead of writing a whole lot of things that our TodoItem shouldn't have to know about.
The payload produced by the TodoActions.create() will look like:
The payload produced by the TodoActions.create() will look like:
```javascript
```javascript
{
{
source: 'VIEW_ACTION',
source: 'VIEW_ACTION',
action: {
action: {
type: 'TODO_CREATE',
type: 'TODO_CREATE',
text: 'Write blog post about Flux'
text: 'Write blog post about Flux'
}
}
}
}
```
```
This payload is provided to the TodoStore through its registered callback. The TodoStore then broadcasts the 'change' event, and the MainSection responds by fetching the new collection of to-do items from the TodoStore and changing its state. This change in state causes the TodoApp component to call its own render() method, and the render() method of all of its descendents.
This payload is provided to the TodoStore through its registered callback. The TodoStore then broadcasts the 'change' event, and the MainSection responds by fetching the new collection of to-do items from the TodoStore and changing its state. This change in state causes the TodoApp component to call its own render() method, and the render() method of all of its descendents.
Start Me Up
Start Me Up
-----------
-----------
The bootstrap file of our application is app.js. It simply takes the TodoApp component and renders it in the root element of the application.
The bootstrap file of our application is app.js. It simply takes the TodoApp component and renders it in the root element of the application.
```javascript
```javascript
/** @jsx React.DOM */
/** @jsx React.DOM */
var React = require('react');
var React = require('react');
var TodoApp = require('./components/TodoApp.react');
var TodoApp = require('./components/TodoApp.react');
React.renderComponent(
React.renderComponent(
<TodoApp/>,
<TodoApp/>,
document.getElementById('todoapp')
document.getElementById('todoapp')
);
);
```
```
Adding Dependency Management to the Dispatcher
Adding Dependency Management to the Dispatcher
----------------------------------------------
----------------------------------------------
As I said previously, our Dispatcher implementation is a bit naive. It's pretty good, but it will not suffice for most applications. We need a way to be able to manage dependencies between Stores. Let's add that functionality with a waitFor() method within the main body of the Dispatcher class.
As I said previously, our Dispatcher implementation is a bit naive. It's pretty good, but it will not suffice for most applications. We need a way to be able to manage dependencies between Stores. Let's add that functionality with a waitFor() method within the main body of the Dispatcher class.
We'll need another public method, waitFor(). Note that it returns a Promise that can in turn be returned from the Store callback.
We'll need another public method, waitFor(). Note that it returns a Promise that can in turn be returned from the Store callback.
@ -590,12 +590,12 @@ We'll need another public method, waitFor(). Note that it returns a Promise that
Now within the TodoStore callback we can explicitly wait for any dependencies to first update before moving forward. However, if Store A waits for Store B, and B waits for A, then a circular dependency will occur. A more robust dispatcher is required to flag this scenario with warnings in the console.
Now within the TodoStore callback we can explicitly wait for any dependencies to first update before moving forward. However, if Store A waits for Store B, and B waits for A, then a circular dependency will occur. A more robust dispatcher is required to flag this scenario with warnings in the console.
The Future of Flux
The Future of Flux
------------------
------------------
A lot of people ask if Facebook will release Flux as an open source framework. Really, Flux is just an architecture, not a framework. But perhaps a Flux boilerplate project might make sense, if there is enough interest. Please let us know if you'd like to see us do this.
A lot of people ask if Facebook will release Flux as an open source framework. Really, Flux is just an architecture, not a framework. But perhaps a Flux boilerplate project might make sense, if there is enough interest. Please let us know if you'd like to see us do this.