flux


The entire reasoning behind Flux is making data changes in your app easy to reason about. In its most simple form, Flux removes the burden of having a component manage its own state and moves that data to a central location called a Store where multiple components can now share that data. Then, everything in that Store’s purpose is to, in one way or another, assist in the modification or preservation of the data. The benefit of this is that if the data in the Store changes, you know exactly what triggered the change because the Store is the only place in your app with the ability to modify the data.
 
Notice how Flux revolves around the fact that your application has data and that data needs to be kept up to date in an effective manner. If your app doesn’t have and or care about dynamic data, Flux might not be the best choice. Flux excels in respect to data management, but will feel unnecessary if you don’t have any data to manage.

So now that you’re aware of the necessity of your app to have data in order for Flux to be beneficial, let’s talk about the flow of a Flux app. Because, again, the whole idea of a Flux app is to make your data changes easy to reason about, Flux embraces a one way data flow architecture.








Let’s start off with our components. Remember, one of the main points of Flux is to take the storage of the data out of the component and put it in a more central location (Store). Because of that, our components will look exactly the same except, as you probably guessed, we’ll be calling out to our Actions to add data and Stores to fetch data.

todo-list example (The full source code can be found HERE.)

ListContainer.jsx

var React = require('react');
var AddItem = require('./AddItem');
var List = require('./List');
var todoStore = require('../stores/todoStore');
var todoActions = require('../actions/todoActions');

var ListContainer = React.createClass({
  getInitialState: function(){
    return {
      list: todoStore.getList()              //comes from store
    }
  },
  componentDidMount: function(){                 // set/unset store listeners
    todoStore.addChangeListener(this._onChange);
  },
  componentWillUnmount: function(){
    todoStore.removeChangeListener(this._onChange);
  },
  handleAddItem: function(newItem){             // add/remove through actions
    todoActions.addItem(newItem);           //instead of setState, action is called.    we first need go through our actions in order to eventually update the data in the store.
  },
  handleRemoveItem: function(index){
    todoActions.removeItem(index);
  },
  _onChange: function(){                                //actual listener for store mounted/unmounted above. (used as callback function when changes happen its called
    this.setState({
      list: todoStore.getList()
    })
  },
  render: function(){
    return (
      <div className="col-sm-6 col-md-offset-3">
        <div className="col-sm-12">
          <h3 className="text-center"> Todo List </h3>
          <AddItem add={this.handleAddItem}/>
          <List items={this.state.list} remove={this.handleRemoveItem}/>
        </div>
      </div>
    )
  }
});

module.exports = ListContainer;


./Actions/todoAction

var AppDispatcher = require('../dispatcher/AppDispatcher');
var appConstants = require('../constants/appConstants');

var todoActions = {
  addItem: function(item){
    AppDispatcher.handleAction({
      actionType: appConstants.ADD_ITEM,
      data: item
    });
  },
  removeItem: function(index){
    AppDispatcher.handleAction({
      actionType: appConstants.REMOVE_ITEM,
      data: index
    })
  }
};

module.exports = todoActions;



Dispatcher is what is going to emit a certain event. Notice the way we keep track of these events is through a constants file. Because we need to emit a specific type of event, and also listen for that exact same type of event, a constants object makes perfect sense.

./constants/appConstants.jsx

var appConstants = {
  ADD_ITEM: "ADD_ITEM",
  REMOVE_ITEM: "REMOVE_ITEM"
};

module.exports = appConstants;

since our Dispatcher is the one actually emitting an event, our Actions object can be thought of as essentially just prepping an object before it’s dispatched
Every time you want to change the data that lives in a store, you must go through the Dispatcher.

./dispatcher/AppDispatcher

var Dispatcher = require('flux').Dispatcher;
var AppDispatcher = new Dispatcher();

AppDispatcher.handleAction = function(action){
  this.dispatch({
    source: 'VIEW_ACTION',     // The reason we’re using VIEW_ACTION as the source of our dispatch is because you could also be using SERVER_ACTION if that action was coming from the server.a
    action: action                        // contains actionType and data
  });
};

module.exports = AppDispatcher;

you’ll use the same dispatcher in all your stores as well in order to register callbacks which will be invoked when certain Dispatches are dispatched

./stores/todoStore

store is where you actually 'store' your data. Store has four parts:
1) the actual "model" / datastore (private): your “model” is just a JavaScript data structure like an object or array.
var _store = {
  list: []
};
2) setter functions: the only interface for manipulating the data of our store
var addItem = function(item){
  _store.list.push(item);
};

var removeItem = function(index){
  _store.list.splice(index, 1);
}
3) getter + register listeners[callback functions for events that happen] (public): 
var todoStore = objectAssign({}, EventEmitter.prototype, {       //objectAssign // Object.assign(target, ...sources) : merges all the source jsons to target
  addChangeListener: function(cb){           // in the listener above we just re-set the state from the store to sync wit it
    this.on(CHANGE_EVENT, cb);              //cb = callback function
  },
  removeChangeListener: function(cb){
    this.removeListener(CHANGE_EVENT, cb);
  },
  getList: function(){
    return _store.list;
  },
});

by objectAssign we merge all the properties of EventEmitter’s prototype object (e.g. on(), emit()) + addChangeListener, removeChangeListener and getList 
4) process an actual dispatched request + emit event type:
AppDispatcher.register(function(payload){
  var action = payload.action;
  switch(action.actionType){
    case appConstants.ADD_ITEM:
      addItem(action.data);
      todoStore.emit(CHANGE_EVENT);
      break;
    case appConstants.REMOVE_ITEM:
      removeItem(action.data);
      todoStore.emit(CHANGE_EVENT);
      break;
    default:
      return true;
  }
});

Remember, when the handleAction method on our Dispatcher is invoked, it’s going to dispatch an object which will look like this
{
  source: ‘VIEW_ACTION’,
  action: {
    actionType: ‘SOME_CONSTANT’,
    data: ‘SOME DATA’
   }
}
So on our Switch statement, we’ll be checking the actionType in order to know which action was emitted. Based on that actionType, we’ll know exactly what setter function (which we created in step 2 above) to invoke in order to modify the data. Once we invoke our setter function, we will then emit a change event, then, because in our addChangeListener method in our todoStore we set up the .on() event handler, whenever it hears the change event, it will invoke the callback function which was passed to it which is _onChange from our original component.


complete store code is as below:
var AppDispatcher = require('../dispatcher/AppDispatcher');
var appConstants = require('../constants/appConstants');
var objectAssign = require('react/lib/Object.assign');
var EventEmitter = require('events').EventEmitter;

var CHANGE_EVENT = 'change';

var _store = {
  list: []
};

var addItem = function(item){
  _store.list.push(item);
};

var removeItem = function(index){
  _store.list.splice(index, 1);
}

var todoStore = objectAssign({}, EventEmitter.prototype, {
  addChangeListener: function(cb){
    this.on(CHANGE_EVENT, cb);
  },
  removeChangeListener: function(cb){
    this.removeListener(CHANGE_EVENT, cb);
  },
  getList: function(){
    return _store.list;
  },
});

AppDispatcher.register(function(payload){
  var action = payload.action;
  switch(action.actionType){
    case appConstants.ADD_ITEM:
      addItem(action.data);
      todoStore.emit(CHANGE_EVENT);
      break;
    case appConstants.REMOVE_ITEM:
      removeItem(action.data);
      todoStore.emit(CHANGE_EVENT);
      break;
    default:
      return true;
  }
});

module.exports = todoStore;

Let’s walk step by step through the whole process now.
- Our todoStore is created, creating a place where our data lives, some setter functions, a todoStore object with a getList method a addChangeListener and removeChangeListener function, and we register our callbacks with the Dispatcher.
- When our component mounts, we pass _.onChange to todoStore.addChangeListener which makes it so whenever the Store hears the “CHANGE” event, it will run _.onChange which will refetch the data from the store using todoStore.getList() and update its own state with the data returned from getList().
- A user clicks “Submit” wanting to add a new item to their todo list which invokes a method on the component which we’ll call handleAddItem.
- handleAddItem runs and invokes the todoActions.addItem method passing it the new item to add.
- The addItem method on our todoActions object invokes AppDispatcher.handleAction passing it an object with the actionType of appConstants.ADD_ITEM and the data of the original todo item.
- When AppDispatcher.handleAction invokes, it emits appConstants.ADD_ITEM, because of step 1, the callback we passed to AppDispatcher.register in our Store is invoked, the switch statement catches the appConstants.ADD_ITEM conditional and then addItem is invoked which pushes the new item onto our data in the Store.
- todoStore emits a “CHANGE” event then because of step 2, todoStore hears that event which invokes the callback function which was passed to it which was originally _onChange in our component
- _onChange runs which uses todoStore.getList() in order to update its own internal state and now we’ve gone full circle.


Think of how data travels in a Flux app, and that patterns remains the same with REST calls. Your action makes a request, gets the data, then dispatches the dispatcher which triggers the store, etc.  And so the data would then be passed from the Action handler to the Dispatcher and then finally to the store where it would be updated and emit the correct event.

ReactJs does not really require any particular AJAX call paradigm. Would you then use some kind of Promise library to make those async calls and require that framework just in the Actions Handler module. Then on the resolve move to the Dispatcher? Is there a best practice suggestion for that kind of action?  - Exactly. I love Promise based HTTP client for the browser and node.js. I like to have a utils file which handles my AJAX requests. So it goes view -> (actions -> utils -> get data) -> dispatcher -> store -> emit change.






Comments