tripleaxis
6/29/2014 - 10:23 PM

A simpler implementation of React.JS's Flux

A simpler implementation of React.JS's Flux

var Reflux = require('./src/reflux');

// Creating an Action
var textUpdate = Reflux.createAction();
var statusUpdate = Reflux.createAction();

// Creating a Data Store - Listening to textUpdate action
var textStore = Reflux.createStore({
    init: function() {
        this.listenTo(textUpdate, this.output);
    },
    output: function() {
        var i, args = Array.prototype.slice.call(arguments, 0);
        for (i = 0; i < args.length; i++) {
            this.writeOut(args[i]);
        }
    },
    writeOut: function(text) {
        this.trigger(text);
    }
});

// Creating a DataStore
var statusStore = Reflux.createStore({
    init: function() {
        this.listenTo(statusUpdate, this.output);
    },
    output: function(flag) {
        var status = flag ? 'ONLINE' : 'OFFLINE';
        this.trigger(status);
    }
});

// Creating an aggregate DataStore that is listening to textStore and statusStore
var storyStore = Reflux.createStore({
    init: function() {
        this.listenTo(statusStore, this.statusChanged);
        this.listenTo(textStore, this.textUpdated);
        this.storyArr = [];
    },
    statusChanged: function(flag) {
        if (flag === 'OFFLINE') {
            this.trigger('Once upon a time the user did the following: ' + this.storyArr.join(', '));
            // empty storyArr
            this.storyArr.splice(0, this.storyArr.length);
        }
    },
    textUpdated: function(text) {
        this.storyArr.push(text);
    }
});

// Fairly simple view component that outputs to console
function ConsoleComponent() {
    textStore.listen(function(text) {
        console.log('text: ', text);
    });
    statusStore.listen(function(status) {
        console.log('status: ', status);
    });
    storyStore.listen(function(story) {
        console.log('story: ', story);
    });
};

var consoleComponent = new ConsoleComponent();

// Invoking the action with arbitrary parameters
statusUpdate(true);
textUpdate("testing", 1337, { "test": 1337 });
statusUpdate(false);

/** Will output the following:
 *
 * status:  ONLINE
 * text:  testing
 * text:  1337
 * text:  { test: 1337 }
 * story:  Once upon a time the user did the following: testing, 1337, [object Object]
 * status:  OFFLINE
 */
var EventEmitter = require('events').EventEmitter,
    _ = require('lodash');

/**
 * Creates an action functor object
 */
exports.createAction = function() {

    var action = new EventEmitter(),
        eventLabel = "action",
        functor;

    functor = function() {
        action.emit(eventLabel, Array.prototype.slice.call(arguments, 0));
    };

    /**
     * Subscribes the given callback for action triggered
     *
     * @param {Function} callback The callback to register as event handler
     * @param {Mixed} [optional] bindContext The context to bind the callback with
     * @returns {Function} Callback that unsubscribes the registered event handler
     */
    functor.listen = function(callback, bindContext) {
        var eventHandler = function(args) {
            callback.apply(bindContext, args);
        };
        action.addListener(eventLabel, eventHandler);

        return function() {
            action.removeListener(eventLabel, eventHandler);
        };
    };

    return functor;

};

/**
 * Creates an event emitting Data Store
 *
 * @param {Object} definition The data store object definition
 */
exports.createStore = function(definition) {
    var store = new EventEmitter(),
        eventLabel = "change";

    function Store() {
        if (this.init && _.isFunction(this.init)) {
            this.init();
        }
    }
    _.assign(Store.prototype, definition);
    Store.prototype.listenTo = function(listenable, callback) {
        if (!_.isFunction(listenable.listen)) {
            throw new TypeError(listenable + " is missing a listen method");
        }
        return listenable.listen(callback, this);
    };
    Store.prototype.listen = function(callback, bindContext) {
        var eventHandler = function(args) {
            callback.apply(bindContext, args);
        };
        store.addListener(eventLabel, eventHandler);

        return function() {
            action.removeListener(eventLabel, eventHandler);
        };
    };
    Store.prototype.trigger = function() {
        var args = Array.prototype.slice.call(arguments, 0);
        store.emit(eventLabel, args);
    };

    return new Store();
};