- {todos}
--- id: flux-todo-list title: Flux TodoMVC Tutorial layout: docs prev: flux-overview.html --- 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](https://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 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. 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: ``` myapp | + ... + js | + app.js + bundle.js // generated by Browserify whenever we make changes. + index.html + ... ``` Next we'll dive into the js directory, and layout our application's primary directory structure: ``` myapp | + ... + js | + actions + components // all React components, both views and controller-views + constants + dispatcher + stores + app.js + bundle.js + index.html + ... ``` 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. ```javascript var Promise = require('es6-promise').Promise; var merge = require('react/lib/merge'); var _callbacks = []; var _promises = []; /** * Add a promise to the queue of callback invocation promises. * @param {function} callback The Store's registered callback. * @param {object} payload The data from the Action. */ var _addPromise = function(callback, payload) { _promises.push(new Promise(function(resolve, reject) { if (callback(payload)) { resolve(payload); } else { reject(new Error('Dispatcher callback unsuccessful')); } })); }; /** * Empty the queue of callback invocation promises. */ var _clearPromises = function() { _promises = []; }; var Dispatcher = function() {}; Dispatcher.prototype = merge(Dispatcher.prototype, { /** * Register a Store's callback so that it may be invoked by an action. * @param {function} callback The callback to be registered. * @return {number} The index of the callback within the _callbacks array. */ register: function(callback) { _callbacks.push(callback); return _callbacks.length - 1; // index }, /** * dispatch * @param {object} payload The data from the action. */ dispatch: function(payload) { _callbacks.forEach(function(callback) { _addPromise(callback, payload); }); Promise.all(_promises).then(_clearPromises); } }); module.exports = Dispatcher; ``` 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. 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 merge = require('react/lib/merge'); var AppDispatcher = merge(Dispatcher.prototype, { /** * A bridge function between the views and the dispatcher, marking the action * as a view action. Another variant here could be handleServerAction. * @param {object} action The data coming from the view. */ handleViewAction: function(action) { this.dispatch({ source: 'VIEW_ACTION', action: action }); } }); 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 ---------------- 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. ```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. * @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 }; } /** * Delete a TODO item. * @param {string} id */ function destroy(id) { delete _todos[id]; } var TodoStore = merge(EventEmitter.prototype, { /** * Get the entire collection of TODOs. * @return {object} */ getAll: function() { return _todos; }, emitChange: function() { this.emit(CHANGE_EVENT); }, /** * @param {function} callback */ addChangeListener: function(callback) { this.on(CHANGE_EVENT, callback); }, /** * @param {function} callback */ removeChangeListener: function(callback) { this.removeListener(CHANGE_EVENT, callback); } dispatcherIndex: AppDispatcher.register(function(payload) { var action = payload.action; var text; switch(action.actionType) { case TodoConstants.TODO_CREATE: text = action.text.trim(); if (text !== '') { create(text); TodoStore.emitChange(); } break; case TodoConstants.TODO_DESTROY: destroy(action.id); break; // add more cases for other actionTypes, like TODO_UPDATE, etc. } return true; // No errors. Needed by promise in Dispatcher. }) }); 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. 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 one actionType, but later we can add as many as we need. 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) ```javascript /** @jsx React.DOM */ var Footer = require('./Footer.react'); var Header = require('./Header.react'); var MainSection = require('./MainSection.react'); var React = require('react'); var TodoStore = require('../stores/TodoStore'); function getTodoState() { return { allTodos: TodoStore.getAll() }; } var TodoApp = React.createClass({ getInitialState: function() { return getTodoState(); }, componentDidMount: function() { TodoStore.addChangeListener(this._onChange); }, componentWillUnmount: function() { TodoStore.removeChangeListener(this._onChange); }, /** * @return {object} */ render: function() { return (