Flux Example

In notebook:
FrontEndMasters React
Created at:
2016-06-17
Updated:
2016-06-17
Tags:
libraries React JavaScript
5-flux/app.js

It pulls in components/App.js 
  //  ****    app.js  ****

var React = require('react');
var App = require('./app/components/App');
React.render(<App/>, document.body);
  //  ****    app/components/App.js   ****

var React = require('react');
var ContactsStore = require('../stores/ContactsStore');
var ViewActionCreators = require('../actions/ViewActionCreators');

var App = React.createClass({
  getInitialState () {
    return ContactsStore.getState();
  },
    
    // when the component first gets rendered this will get called
  componentDidMount () {
    //   adds listener
    ContactsStore.addChangeListener(this.handleStoreChange);
    // then we call ActionCreators (an Action Creator, see diagram)
    ViewActionCreators.loadContacts();
  },

  componentWillUnmount () {
    ContactsStore.removeChangeListener(this.handleStoreChange);
  },

  handleStoreChange () {
    //   this gets called in the above change ChangeListener
    this.setState(ContactsStore.getState());
  },

  renderContacts () {
    return this.state.contacts.map((contact) => {
      return <li>{contact.first} {contact.last}</li>;
    });
  },

  render () {
    if (!this.state.loaded) {
      return <div>Loading...</div>;
    }

    return (
      <div>
        <ul>{this.renderContacts()}</ul>
      </div>
    );
  }
});

module.exports = App;

in app/actions/ViewActionCreators we call the Dispatcher:
  var { ActionTypes } = require('../Constants');
var AppDispatcher = require('../AppDispatcher');
var ApiUtil = require('../utils/ApiUtil');

var ViewActionCreators = {
  loadContacts () {
    //   tells the _Dispatcher_
    AppDispatcher.handleViewAction({
      type: ActionTypes.LOAD_CONTACTS
    });
    // loads contacts
    ApiUtil.loadContacts();
  }
};

module.exports = ViewActionCreators;

ApiUtils takes care of the server communications:
  var xhr = require('../lib/xhr');
var { API, ActionTypes } = require('../Constants');
var ServerActionCreators = require('../actions/ServerActionCreators');

var ApiUtils = {
  loadContacts () {
    xhr.getJSON(`${API}/contacts`, (err, res) => {
        // callback when the response comes back
        // call a new action (ServerActionCreators) with loadedContacts
      ServerActionCreators.loadedContacts(res.contacts);
    });
  }
};

module.exports = ApiUtils;

then ServerActionCreators gets called:
  var { ActionTypes } = require('../Constants');
var AppDispatcher = require('../AppDispatcher');

var ServerActionCreators = {
    // again, notify the _Dispatcher_ (when contacts are loaded)
  loadedContacts (contacts) {
    AppDispatcher.handleServerAction({
      type: ActionTypes.CONTACTS_LOADED,
      contacts: contacts
    });
  }
};

module.exports = ServerActionCreators;
Note that the two actions will load contacts and ​loadContacts​ are totally independent. They happen one after the other, but are not coupled. 

Note also that none of the elements presented so far have kept the data yet. 

Move to callback Flux element. The callback is in the store:
stores/ContactSores.js
  var AppDispatcher = require('../AppDispatcher');
var { EventEmitter } = require('events');
var { ActionTypes } = require('../Constants');
var assign = require('react/lib/Object.assign');

var events = new EventEmitter();
var CHANGE_EVENT = 'CHANGE';

// the internal state in the store 
var state = {
  contacts: [],
  loaded: false
};

var setState = (newState) => {
  assign(state, newState);
//   emit our _Event_ (see diagram, triggers in View (components/App.js)
  events.emit(CHANGE_EVENT);
};

var ContactsStore = {
  addChangeListener (fn) {
    events.addListener(CHANGE_EVENT, fn);
  },

  removeChangeListener (fn) {
    events.removeListener(CHANGE_EVENT, fn);
  },

  getState () {
    return state;
  }
};

// register the _Callback_ at the _Dispatcher_
ContactsStore.dispatchToken = AppDispatcher.register((payload) => {
  var { action } = payload;
  console.log(action.type);
//   this is where we define which action types we care about
  if (action.type === ActionTypes.CONTACTS_LOADED) {
    //   here, `setState` is not a semantics of React, it's a custom handler
    setState({
      loaded: true,
      contacts: action.contacts
    });
  }
});

module.exports = ContactsStore;
components/App.js listens to stores change event:
  // **** components/App.js   ****

var App = React.createClass({
  getInitialState () {
    return ContactsStore.getState();
  },
    
  componentDidMount () {
    //   listen to _Store_ changes
    ContactsStore.addChangeListener(this.handleStoreChange);
    ViewActionCreators.loadContacts();
  },
  
  componentWillUnmount () {
    ContactsStore.removeChangeListener(this.handleStoreChange);
  },
    // handle the changes
  handleStoreChange () {
    this.setState(ContactsStore.getState());
  },
  
  ..
This closes the circle. - ContactStore.dispatchToken ?
In case if one store depends on another store and it has to wait for the other one to finish its calculations first.