Fork me on GitHub
Fluxxor

Quick-Start Guide / Basic Todos Example

Once you have Fluxxor and React installed, it's time to build an app! Since this guide is designed to cover the basics quickly, we'll start with a very basic todo app—namely, this one right here:

    Stores and Actions

    First, let's create a store to keep track of our todo items. It will respond to the following actions:

    var constants = {
      ADD_TODO: "ADD_TODO",
      TOGGLE_TODO: "TOGGLE_TODO",
      CLEAR_TODOS: "CLEAR_TODOS"
    };
    
    var TodoStore = Fluxxor.createStore({
      initialize: function() {
        this.todoId = 0;
        this.todos = {};
    
        this.bindActions(
          constants.ADD_TODO, this.onAddTodo,
          constants.TOGGLE_TODO, this.onToggleTodo,
          constants.CLEAR_TODOS, this.onClearTodos
        );
      },
    
      onAddTodo: function(payload) {
        var id = this._nextTodoId();
        var todo = {
          id: id,
          text: payload.text,
          complete: false
        };
        this.todos[id] = todo;
        this.emit("change");
      },
    
      onToggleTodo: function(payload) {
        var id = payload.id;
        this.todos[id].complete = !this.todos[id].complete;
        this.emit("change");
      },
    
      onClearTodos: function() {
        var todos = this.todos;
    
        Object.keys(todos).forEach(function(key) {
          if(todos[key].complete) {
            delete todos[key];
          }
        });
    
        this.emit("change");
      },
    
      getState: function() {
        return {
          todos: this.todos
        };
      },
    
      _nextTodoId: function() {
        return ++this.todoId;
      }
    });
    

    Let's create a few semantic actions to go along with our action types.

    var actions = {
      addTodo: function(text) {
        this.dispatch(constants.ADD_TODO, {text: text});
      },
    
      toggleTodo: function(id) {
        this.dispatch(constants.TOGGLE_TODO, {id: id});
      },
    
      clearTodos: function() {
        this.dispatch(constants.CLEAR_TODOS);
      }
    };
    

    Now we can instantiate our store and build a Flux instance:

    var stores = {
      TodoStore: new TodoStore()
    };
    
    var flux = new Fluxxor.Flux(stores, actions);
    

    Finally, let's use the "dispatch" event to add some logging:

    flux.on("dispatch", function(type, payload) {
      if (console && console.log) {
        console.log("[Dispatch]", type, payload);
      }
    });
    

    React Application

    Let's build out our UI with React.

    Our top-level Application component will use the FluxMixin as well as the StoreWatchMixin to make our lives a bit easier. The component will iterate over the array of todos and emit a TodoItem component for each one.

    We'll also add a quick form for adding new todo items, and a button for clearing completed todos.

    var FluxMixin = Fluxxor.FluxMixin(React),
        StoreWatchMixin = Fluxxor.StoreWatchMixin;
    
    var Application = React.createClass({
      mixins: [FluxMixin, StoreWatchMixin("TodoStore")],
    
      getInitialState: function() {
        return { newTodoText: "" };
      },
    
      getStateFromFlux: function() {
        var flux = this.getFlux();
        // Our entire state is made up of the TodoStore data. In a larger
        // application, you will likely return data from multiple stores, e.g.:
        //
        //   return {
        //     todoData: flux.store("TodoStore").getState(),
        //     userData: flux.store("UserStore").getData(),
        //     fooBarData: flux.store("FooBarStore").someMoreData()
        //   };
        return flux.store("TodoStore").getState();
      },
    
      render: function() {
        var todos = this.state.todos;
        return (
          <div>
            <ul>
              {Object.keys(todos).map(function(id) {
                return <li key={id}><TodoItem todo={todos[id]} /></li>;
              })}
            </ul>
            <form onSubmit={this.onSubmitForm}>
              <input type="text" size="30" placeholder="New Todo"
                     value={this.state.newTodoText}
                     onChange={this.handleTodoTextChange} />
              <input type="submit" value="Add Todo" />
            </form>
            <button onClick={this.clearCompletedTodos}>Clear Completed</button>
          </div>
        );
      },
    
      handleTodoTextChange: function(e) {
        this.setState({newTodoText: e.target.value});
      },
    
      onSubmitForm: function(e) {
        e.preventDefault();
        if (this.state.newTodoText.trim()) {
          this.getFlux().actions.addTodo(this.state.newTodoText);
          this.setState({newTodoText: ""});
        }
      },
    
      clearCompletedTodos: function(e) {
        this.getFlux().actions.clearTodos();
      }
    });
    

    The TodoItem component will display and style itself based on the completion of the todo, and will dispatch an action indicating its intent to toggle its completion state.

    var TodoItem = React.createClass({
      mixins: [FluxMixin],
    
      propTypes: {
        todo: React.PropTypes.object.isRequired
      },
    
      render: function() {
        var style = {
          textDecoration: this.props.todo.complete ? "line-through" : ""
        };
    
        return <span style={style} onClick={this.onClick}>{this.props.todo.text}</span>;
      },
    
      onClick: function() {
        this.getFlux().actions.toggleTodo(this.props.todo.id);
      }
    });
    

    Bringing it Together

    Now that we have a Flux instance and all our components are defined, we can finally render our app. We'll put it inside a div in our HTML with an ID of "app".

    React.render(<Application flux={flux} />, document.getElementById("app"));
    

    And that's it! We've created a (super simple) Flux application with React and Fluxxor. You can find the full source code on GitHub.


    See a typo? Something still not clear? Report an issue on GitHub.