Comparing version 5.0.4 to 6.0.0
{ | ||
"name": "reflux", | ||
"version": "5.0.4", | ||
"version": "6.0.0", | ||
"homepage": "https://github.com/spoike/reflux", | ||
@@ -5,0 +5,0 @@ "authors": [ |
@@ -7,2 +7,9 @@ # Changelog | ||
## v6.0.0 | ||
* Switch to new docs. | ||
* Updated to `reflux-core` v1.0.0. | ||
* Allow complex definitions for child actions instead of just string for action name. | ||
* Breaking change: actions are sync by default unless child actions are involved. | ||
## v5.0.4 | ||
@@ -9,0 +16,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.Reflux=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){"use strict";function d(a,b,c){this.fn=a,this.context=b,this.once=c||!1}function e(){}var f=Object.prototype.hasOwnProperty,g="function"!=typeof Object.create?"~":!1;e.prototype._events=void 0,e.prototype.eventNames=function(){var a,b=this._events,c=[];if(!b)return c;for(a in b)f.call(b,a)&&c.push(g?a.slice(1):a);return Object.getOwnPropertySymbols?c.concat(Object.getOwnPropertySymbols(b)):c},e.prototype.listeners=function(a,b){var c=g?g+a:a,d=this._events&&this._events[c];if(b)return!!d;if(!d)return[];if(d.fn)return[d.fn];for(var e=0,f=d.length,h=new Array(f);f>e;e++)h[e]=d[e].fn;return h},e.prototype.emit=function(a,b,c,d,e,f){var h=g?g+a:a;if(!this._events||!this._events[h])return!1;var i,j,k=this._events[h],l=arguments.length;if("function"==typeof k.fn){switch(k.once&&this.removeListener(a,k.fn,void 0,!0),l){case 1:return k.fn.call(k.context),!0;case 2:return k.fn.call(k.context,b),!0;case 3:return k.fn.call(k.context,b,c),!0;case 4:return k.fn.call(k.context,b,c,d),!0;case 5:return k.fn.call(k.context,b,c,d,e),!0;case 6:return k.fn.call(k.context,b,c,d,e,f),!0}for(j=1,i=new Array(l-1);l>j;j++)i[j-1]=arguments[j];k.fn.apply(k.context,i)}else{var m,n=k.length;for(j=0;n>j;j++)switch(k[j].once&&this.removeListener(a,k[j].fn,void 0,!0),l){case 1:k[j].fn.call(k[j].context);break;case 2:k[j].fn.call(k[j].context,b);break;case 3:k[j].fn.call(k[j].context,b,c);break;default:if(!i)for(m=1,i=new Array(l-1);l>m;m++)i[m-1]=arguments[m];k[j].fn.apply(k[j].context,i)}}return!0},e.prototype.on=function(a,b,c){var e=new d(b,c||this),f=g?g+a:a;return this._events||(this._events=g?{}:Object.create(null)),this._events[f]?this._events[f].fn?this._events[f]=[this._events[f],e]:this._events[f].push(e):this._events[f]=e,this},e.prototype.once=function(a,b,c){var e=new d(b,c||this,!0),f=g?g+a:a;return this._events||(this._events=g?{}:Object.create(null)),this._events[f]?this._events[f].fn?this._events[f]=[this._events[f],e]:this._events[f].push(e):this._events[f]=e,this},e.prototype.removeListener=function(a,b,c,d){var e=g?g+a:a;if(!this._events||!this._events[e])return this;var f=this._events[e],h=[];if(b)if(f.fn)(f.fn!==b||d&&!f.once||c&&f.context!==c)&&h.push(f);else for(var i=0,j=f.length;j>i;i++)(f[i].fn!==b||d&&!f[i].once||c&&f[i].context!==c)&&h.push(f[i]);return h.length?this._events[e]=1===h.length?h[0]:h:delete this._events[e],this},e.prototype.removeAllListeners=function(a){return this._events?(a?delete this._events[g?g+a:a]:this._events=g?{}:Object.create(null),this):this},e.prototype.off=e.prototype.removeListener,e.prototype.addListener=e.prototype.on,e.prototype.setMaxListeners=function(){return this},e.prefixed=g,"undefined"!=typeof b&&(b.exports=e)},{}],2:[function(a,b,c){"use strict";Object.defineProperty(c,"__esModule",{value:!0})},{}],3:[function(a,b,c){"use strict";function d(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:!0;h=a}function e(a){h&&i.push(a)}function f(a){h&&j.push(a)}function g(){for(;i.length;)i.pop();for(;j.length;)j.pop()}Object.defineProperty(c,"__esModule",{value:!0});var h=!1,i=[],j=[];c.useKeep=d,c.addStore=e,c.addAction=f,c.createdStores=i,c.createdActions=j,c.reset=g},{}],4:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}Object.defineProperty(c,"__esModule",{value:!0}),c.joinStrict=c.joinConcat=c.joinLeading=c.joinTrailing=c.fetchInitialState=c.stopListeningToAll=c.stopListeningTo=c.listenTo=c.validateListening=c.listenToMany=c.hasListener=void 0;var e=a("./utils"),f=d(e),g=a("./joins"),h=function(a){for(var b,c=0,d={};c<(a.children||[]).length;++c)b=a.children[c],a[b]&&(d[b]=a[b]);return d},i=function j(a){var b={};for(var c in a){var d=a[c],e=h(d),g=j(e);b[c]=d;for(var i in g){var k=g[i];b[c+f.capitalize(i)]=k}}return b};c.hasListener=function(a){for(var b,c,d,e=0;e<(this.subscriptions||[]).length;++e)for(d=[].concat(this.subscriptions[e].listenable),b=0;b<d.length;b++)if(c=d[b],c===a||c.hasListener&&c.hasListener(a))return!0;return!1},c.listenToMany=function(a){var b=i(a);for(var c in b){var d=f.callbackName(c),e=this[d]?d:this[c]?c:void 0;e&&this.listenTo(b[c],e,this[d+"Default"]||this[e+"Default"]||e)}},c.validateListening=function(a){return a===this?"Listener is not able to listen to itself":f.isFunction(a.listen)?a.hasListener&&a.hasListener(this)?"Listener cannot listen to this listenable because of circular loop":void 0:a+" is missing a listen method"},c.listenTo=function(a,b,c){var d,e,g,h=this.subscriptions=this.subscriptions||[];return f.throwIf(this.validateListening(a)),this.fetchInitialState(a,c),d=a.listen(this[b]||b,this),e=function(){var a=h.indexOf(g);f.throwIf(-1===a,"Tried to remove listen already gone from subscriptions list!"),h.splice(a,1),d()},g={stop:e,listenable:a},h.push(g),g},c.stopListeningTo=function(a){for(var b,c=0,d=this.subscriptions||[];c<d.length;c++)if(b=d[c],b.listenable===a)return b.stop(),f.throwIf(-1!==d.indexOf(b),"Failed to remove listen from subscriptions list!"),!0;return!1},c.stopListeningToAll=function(){for(var a,b=this.subscriptions||[];a=b.length;)b[0].stop(),f.throwIf(b.length!==a-1,"Failed to remove listen from subscriptions list!")},c.fetchInitialState=function(a,b){b=b&&this[b]||b;var c=this;if(f.isFunction(b)&&f.isFunction(a.getInitialState)){var d=a.getInitialState();d&&f.isFunction(d.then)?d.then(function(){b.apply(c,arguments)}):b.call(this,d)}},c.joinTrailing=(0,g.instanceJoinCreator)("last"),c.joinLeading=(0,g.instanceJoinCreator)("first"),c.joinConcat=(0,g.instanceJoinCreator)("all"),c.joinStrict=(0,g.instanceJoinCreator)("strict")},{"./joins":11,"./utils":13}],5:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}Object.defineProperty(c,"__esModule",{value:!0}),c.deferWith=c.triggerAsync=c.trigger=c.listen=c.shouldEmit=c.preEmit=void 0;var e=a("./utils"),f=d(e);c.preEmit=function(){},c.shouldEmit=function(){return!0},c.listen=function(a,b){b=b||this;var c=function(c){e||a.apply(b,c)},d=this,e=!1;return this.emitter.addListener(this.eventLabel,c),function(){e=!0,d.emitter.removeListener(d.eventLabel,c)}},c.trigger=function(){var a=arguments,b=this.preEmit.apply(this,a);a=void 0===b?a:f.isArguments(b)?b:[].concat(b),this.shouldEmit.apply(this,a)&&this.emitter.emit(this.eventLabel,a)},c.triggerAsync=function(){var a=arguments,b=this;f.nextTick(function(){b.trigger.apply(b,a)})},c.deferWith=function(a){var b=this.trigger,c=this,d=function(){b.apply(c,arguments)};this.trigger=function(){a.apply(c,[d].concat([].splice.call(arguments,0)))}}},{"./utils":13}],6:[function(a,b,c){arguments[4][2][0].apply(c,arguments)},{dup:2}],7:[function(a,b,c){"use strict";function d(a,b){for(var c in b)if(Object.getOwnPropertyDescriptor&&Object.defineProperty){var d=Object.getOwnPropertyDescriptor(b,c);if(!d.value||"function"!=typeof d.value||!b.hasOwnProperty(c))continue;a[c]=b[c].bind(a)}else{var e=b[c];if("function"!=typeof e||!b.hasOwnProperty(c))continue;a[c]=e.bind(a)}return a}Object.defineProperty(c,"__esModule",{value:!0}),c.bindMethods=d},{}],8:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){a=a||{},g.isObject(a)||(a={actionName:a});for(var b in i)if(!n[b]&&k[b])throw new Error("Cannot override API method "+b+" in Reflux.ActionMethods. Use another method name or override it on Reflux.PublisherMethods instead.");for(var c in a)if(!n[c]&&k[c])throw new Error("Cannot override API method "+c+" in action creation. Use another method name or override it on Reflux.PublisherMethods instead.");a.children=a.children||[],a.asyncResult&&(a.children=a.children.concat(["completed","failed"]));for(var d=0,f={};d<a.children.length;d++){var h=a.children[d];f[h]=e(h)}var j=g.extend({eventLabel:"action",emitter:new g.EventEmitter,_isAction:!0},k,i,a),l=function o(){var a=o.sync?"trigger":"triggerAsync";return o[a].apply(o,arguments)};return g.extend(l,f,j),m.addAction(l),l}Object.defineProperty(c,"__esModule",{value:!0}),c.createAction=e;var f=a("./utils"),g=d(f),h=a("./ActionMethods"),i=d(h),j=a("./PublisherMethods"),k=d(j),l=a("./Keep"),m=d(l),n={preEmit:1,shouldEmit:1}},{"./ActionMethods":2,"./Keep":3,"./PublisherMethods":5,"./utils":13}],9:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){function b(){var b,c=0;if(this.subscriptions=[],this.emitter=new g.EventEmitter,this.eventLabel="change",(0,k.bindMethods)(this,a),this.init&&g.isFunction(this.init)&&this.init(),this.listenables)for(b=[].concat(this.listenables);c<b.length;c++)this.listenToMany(b[c])}a=a||{};for(var c in m)if(!r[c]&&(o[c]||q[c]))throw new Error("Cannot override API method "+c+" in Reflux.StoreMethods. Use another method name or override it on Reflux.PublisherMethods / Reflux.ListenerMethods instead.");for(var d in a)if(!r[d]&&(o[d]||q[d]))throw new Error("Cannot override API method "+d+" in store creation. Use another method name or override it on Reflux.PublisherMethods / Reflux.ListenerMethods instead.");a=(0,j.mix)(a),g.extend(b.prototype,q,o,m,a);var e=new b;return i.addStore(e),e}Object.defineProperty(c,"__esModule",{value:!0}),c.createStore=e;var f=a("./utils"),g=d(f),h=a("./Keep"),i=d(h),j=a("./mixer"),k=a("./bindMethods"),l=a("./StoreMethods"),m=d(l),n=a("./PublisherMethods"),o=d(n),p=a("./ListenerMethods"),q=d(p),r={preEmit:1,shouldEmit:1}},{"./Keep":3,"./ListenerMethods":4,"./PublisherMethods":5,"./StoreMethods":6,"./bindMethods":7,"./mixer":12,"./utils":13}],10:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){r.EventEmitter=a}function f(a){r.nextTick=a}function g(a){a(this)}Object.defineProperty(c,"__esModule",{value:!0}),c.__keep=c.joinConcat=c.joinStrict=c.joinLeading=c.all=c.joinTrailing=c.use=c.nextTick=c.setEventEmitter=c.createActions=c.createStore=c.createAction=c.utils=c.StoreMethods=c.PublisherMethods=c.ListenerMethods=c.ActionMethods=c.version=void 0;var h=a("./ActionMethods"),i=d(h),j=a("./ListenerMethods"),k=d(j),l=a("./PublisherMethods"),m=d(l),n=a("./StoreMethods"),o=d(n),p=a("./joins"),q=a("./utils"),r=d(q),s=a("./createAction"),t=a("./createStore"),u=a("./Keep"),v=d(u),w={"reflux-core":"0.4.2"},x=(0,p.staticJoinCreator)("last"),y=x,z=(0,p.staticJoinCreator)("first"),A=(0,p.staticJoinCreator)("strict"),B=(0,p.staticJoinCreator)("all"),C=r,D=function(){var a=function(a,b){Object.keys(a).forEach(function(c){var d=a[c];b[c]=(0,s.createAction)(d)})};return function(b){var c={};return b instanceof Array?b.forEach(function(b){r.isObject(b)?a(b,c):c[b]=(0,s.createAction)(b)}):a(b,c),c}}();c.version=w,c.ActionMethods=i,c.ListenerMethods=k,c.PublisherMethods=m,c.StoreMethods=o,c.utils=C,c.createAction=s.createAction,c.createStore=t.createStore,c.createActions=D,c.setEventEmitter=e,c.nextTick=f,c.use=g,c.joinTrailing=x,c.all=y,c.joinLeading=z,c.joinStrict=A,c.joinConcat=B,c.__keep=v,Object.defineProperty(c,"default",{get:function(){return c}}),Function.prototype.bind||console.error("Function.prototype.bind not available. ES5 shim required. https://github.com/spoike/refluxjs#es5")},{"./ActionMethods":2,"./Keep":3,"./ListenerMethods":4,"./PublisherMethods":5,"./StoreMethods":6,"./createAction":8,"./createStore":9,"./joins":11,"./utils":13}],11:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return function(){var b=n.call(arguments);return(0,k.createStore)({init:function(){this[o[a]].apply(this,b.concat("triggerAsync"))}})}}function f(a){return function(){m.throwIf(arguments.length<2,"Cannot create a join with less than 2 listenables!");var b,c,d=n.call(arguments),e=d.pop(),f=d.length,j={numberOfListenables:f,callback:this[e]||e,listener:this,strategy:a},k=[];for(b=0;f>b;b++)m.throwIf(this.validateListening(d[b]));for(b=0;f>b;b++)k.push(d[b].listen(i(b,j),this));return h(j),c={listenable:d},c.stop=g(c,k,this),this.subscriptions=(this.subscriptions||[]).concat(c),c}}function g(a,b,c){return function(){var d,e=c.subscriptions,f=e?e.indexOf(a):-1;for(m.throwIf(-1===f,"Tried to remove join already gone from subscriptions list!"),d=0;d<b.length;d++)b[d]();e.splice(f,1)}}function h(a){a.listenablesEmitted=new Array(a.numberOfListenables),a.args=new Array(a.numberOfListenables)}function i(a,b){return function(){var c=n.call(arguments);if(b.listenablesEmitted[a])switch(b.strategy){case"strict":throw new Error("Strict join failed because listener triggered twice.");case"last":b.args[a]=c;break;case"all":b.args[a].push(c)}else b.listenablesEmitted[a]=!0,b.args[a]="all"===b.strategy?[c]:c;j(b)}}function j(a){for(var b=0;b<a.numberOfListenables;b++)if(!a.listenablesEmitted[b])return;a.callback.apply(a.listener,a.args),h(a)}Object.defineProperty(c,"__esModule",{value:!0}),c.staticJoinCreator=e,c.instanceJoinCreator=f;var k=a("./createStore"),l=a("./utils"),m=d(l),n=Array.prototype.slice,o={strict:"joinStrict",first:"joinLeading",last:"joinTrailing",all:"joinConcat"}},{"./createStore":9,"./utils":13}],12:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){var b={init:[],preEmit:[],shouldEmit:[]},c=function d(a){var c={};return a.mixins&&a.mixins.forEach(function(a){g.extend(c,d(a))}),g.extend(c,a),Object.keys(b).forEach(function(c){a.hasOwnProperty(c)&&b[c].push(a[c])}),c}(a);return b.init.length>1&&(c.init=function(){var a=arguments;b.init.forEach(function(b){b.apply(this,a)},this)}),b.preEmit.length>1&&(c.preEmit=function(){return b.preEmit.reduce(function(a,b){var c=b.apply(this,a);return void 0===c?a:[c]}.bind(this),arguments)}),b.shouldEmit.length>1&&(c.shouldEmit=function(){var a=arguments;return!b.shouldEmit.some(function(b){return!b.apply(this,a)},this)}),Object.keys(b).forEach(function(a){1===b[a].length&&(c[a]=b[a][0])}),c}Object.defineProperty(c,"__esModule",{value:!0}),c.mix=e;var f=a("./utils"),g=d(f)},{"./utils":13}],13:[function(a,b,c){"use strict";function d(a){return a.charAt(0).toUpperCase()+a.slice(1)}function e(a,b){return b=b||"on",b+c.capitalize(a)}function f(a){var b="undefined"==typeof a?"undefined":m(a);return"function"===b||"object"===b&&!!a}function g(a){if(!f(a))return a;for(var b,c,d,e=1,g=arguments.length;g>e;e++){b=arguments[e],c=Object.keys(b);for(var h=0;h<c.length;h++)if(d=c[h],Object.getOwnPropertyDescriptor&&Object.defineProperty){var i=Object.getOwnPropertyDescriptor(b,d);Object.defineProperty(a,d,i)}else a[d]=b[d]}return a}function h(a){return"function"==typeof a}function i(a){setTimeout(a,0)}function j(a,b){for(var c={},d=0;d<a.length;d++)c[a[d]]=b[d];return c}function k(a){return"object"===("undefined"==typeof a?"undefined":m(a))&&"callee"in a&&"number"==typeof a.length}function l(a,b){if(a)throw Error(b||a)}Object.defineProperty(c,"__esModule",{value:!0});var m="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a};c.capitalize=d,c.callbackName=e,c.isObject=f,c.extend=g,c.isFunction=h,c.nextTick=i,c.object=j,c.isArguments=k,c.throwIf=l,c.EventEmitter=a("eventemitter3")},{eventemitter3:1}],14:[function(a,b,c){var d=a("reflux-core/lib/utils"),e=a("reflux-core/lib/ListenerMethods");b.exports=d.extend({componentWillUnmount:e.stopListeningToAll},e)},{"reflux-core/lib/ListenerMethods":4,"reflux-core/lib/utils":13}],15:[function(a,b,c){var d=a("reflux-core");if(d.defineReact=a("./defineReact"),"undefined"!=typeof React&&React)d.defineReact(React);else try{var e=a("react");d.defineReact(e)}catch(f){}},{"./defineReact":18,react:void 0,"reflux-core":10}],16:[function(a,b,c){var d=a("reflux-core/lib/ListenerMethods"),e=a("./ListenerMixin"),f=a("reflux-core/lib/utils");b.exports=function(a,b){return f.throwIf("undefined"==typeof b,"Reflux.connect() requires a key."),{getInitialState:function(){return f.isFunction(a.getInitialState)?f.object([b],[a.getInitialState()]):{}},componentDidMount:function(){var c=this;f.extend(c,d),this.listenTo(a,function(a){c.setState(f.object([b],[a]))})},componentWillUnmount:e.componentWillUnmount}}},{"./ListenerMixin":14,"reflux-core/lib/ListenerMethods":4,"reflux-core/lib/utils":13}],17:[function(a,b,c){var d=a("reflux-core/lib/ListenerMethods"),e=a("./ListenerMixin"),f=a("reflux-core/lib/utils");b.exports=function(a,b,c){return f.throwIf(f.isFunction(b),"Reflux.connectFilter() requires a key."),{getInitialState:function(){if(!f.isFunction(a.getInitialState))return{};var d=c.call(this,a.getInitialState());return"undefined"!=typeof d?f.object([b],[d]):{}},componentDidMount:function(){var e=this;f.extend(this,d),this.listenTo(a,function(a){var d=c.call(e,a);e.setState(f.object([b],[d]))})},componentWillUnmount:e.componentWillUnmount}}},{"./ListenerMixin":14,"reflux-core/lib/ListenerMethods":4,"reflux-core/lib/utils":13}],18:[function(a,b,c){function d(a,b,c){var k,l;try{h=a||h||React,l=c||h.Component}catch(m){return}if(h&&l&&(!j||c)){var n=function(a){l.call(this,a)};if(f(n,l),k=n.prototype,k.storeKeys=null,k.componentWillMount=function(){if(this.store&&(Array.isArray(this.stores)?this.stores.unshift(this.store):this.stores=[this.store]),this.stores){this.__storeunsubscribes__=this.__storeunsubscribes__||[];for(var a=this.setState.bind(this),b=function(b){var c=e(this.storeKeys,b);c&&a(c)}.bind(this),c=0,d=this.stores.length;d>c;c++){var f=this.stores[c];if("function"==typeof f){var g=f.id;if(f.singleton||(f.singleton=new f,g&&(i.stores[g]=f.singleton)),this.stores[c]=f=f.singleton,f.id=g,g&&i.GlobalState[g]){for(var h in i.GlobalState[g])f.state[h]=i.GlobalState[g][h];i.GlobalState[g]=f.state}else g&&(i.GlobalState[g]=f.state)}this.__storeunsubscribes__.push(f.listen(b));var j=e(this.storeKeys,f.state);j&&this.setState(j)}}this.__readytomap__=!0;var k=this.__delayedmaps__;if(k)for(var l=0,m=k.length;m>l;l++)k[l].func(k[l].state);this.__delayedmaps__=null},k.componentWillUnmount=function(){for(var a=0,b=this.__storeunsubscribes__.length;b>a;a++)this.__storeunsubscribes__[a]();this.__readytomap__=!1},k.mapStoreToState=function(a,b){function c(a){var c=b.call(d,a);if(c){var e=!1;for(var f in c){e=!0;break}e&&d.setState(c)}}"function"==typeof a&&(a=a.singleton?a.singleton:i.initStore(a));var d=this;this.__storeunsubscribes__=this.__storeunsubscribes__||[],this.__storeunsubscribes__.push(a.listen(c)),this.__readytomap__?c(a.state):(this.__delayedmaps__=this.__delayedmaps__||[],this.__delayedmaps__.push({func:c,state:a.state}))},n.extend=function(a){return d(null,null,a)},c)return n;i.Component=n;var o=function(){this.__store__=i.createStore(),this.state={};var a=this;for(var b in this.__store__)!function(b){Object.defineProperty(a,b,{get:function(){return a.__store__[b]},set:function(c){a.__store__[b]=c}})}(b)};k=o.prototype,Object.defineProperty(k,"listenables",{get:function(){return this.__listenables__},set:function(a){var b={};Array.isArray(a)?a.forEach(function(a){for(var c in a)b[c]=a[c]}):b=a,this.__listenables__=b,this.listenToMany(b)},enumerable:!0,configurable:!0}),k.setState=function(a){for(var b in a)this.state[b]=a[b];this.id&&(i.GlobalState[this.id]=this.state),this.trigger(a)},Object.defineProperty(o,"isES6Store",{get:function(){return!0},enumerable:!0,configurable:!0}),i.Store=o,i.GlobalState=i.GlobalState||{},i.stores={},i.getGlobalState=function(){return g(i.GlobalState)},i.setGlobalState=function(a){for(var b in a)i.stores[b]?i.stores[b].setState(a[b]):i.GlobalState[b]=a[b]},i.initializeGlobalStore=i.initStore=function(a){var b=a.id;if(a.singleton)return a.singleton;if(!b)return a.singleton=new a,a.singleton;var c=a.singleton=new a;if(i.stores[b]=c,c.id=b,i.GlobalState[b]){for(var d in i.GlobalState[b])c.state[d]=i.GlobalState[b][d];i.GlobalState[b]=c.state}else i.GlobalState[b]=c.state;return c},j=!0}}function e(a,b){if(!a)return b;for(var c=!1,d={},e=0,f=a.length;f>e;e++){var g=a[e];b.hasOwnProperty(g)&&(c=!0,d[g]=b[g])}return c?d:!1}function f(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);a.prototype=null===b?Object.create(b):(c.prototype=b.prototype,new c)}function g(a,b){if(null===a||"object"!=typeof a)return a;if(a.constructor!==Object&&a.constructor!==Array)return a;if(a.constructor===Date||a.constructor===RegExp||a.constructor===Function||a.constructor===String||a.constructor===Number||a.constructor===Boolean)return new a.constructor(a);b=b||new a.constructor;for(var c in a)b[c]="undefined"==typeof b[c]?g(a[c],null):b[c];return b}var h,i=a("reflux-core"),j=!1;b.exports=d},{"reflux-core":10}],19:[function(a,b,c){var d=a("reflux-core");d.connect=a("./connect"),d.connectFilter=a("./connectFilter"),d.ListenerMixin=a("./ListenerMixin"),d.listenTo=a("./listenTo"),d.listenToMany=a("./listenToMany"),a("./addES6"),b.exports=d},{"./ListenerMixin":14,"./addES6":15,"./connect":16,"./connectFilter":17,"./listenTo":20,"./listenToMany":21,"reflux-core":10}],20:[function(a,b,c){var d=a("reflux-core/lib/ListenerMethods");b.exports=function(a,b,c){return{componentDidMount:function(){for(var e in d)if(this[e]!==d[e]){if(this[e])throw"Can't have other property '"+e+"' when using Reflux.listenTo!";this[e]=d[e]}this.listenTo(a,b,c)},componentWillUnmount:d.stopListeningToAll}}},{"reflux-core/lib/ListenerMethods":4}],21:[function(a,b,c){var d=a("reflux-core/lib/ListenerMethods");b.exports=function(a){return{componentDidMount:function(){for(var b in d)if(this[b]!==d[b]){if(this[b])throw"Can't have other property '"+b+"' when using Reflux.listenToMany!";this[b]=d[b]}this.listenToMany(a)},componentWillUnmount:d.stopListeningToAll}}},{"reflux-core/lib/ListenerMethods":4}]},{},[19])(19)}); | ||
!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.Reflux=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){"use strict";function d(a,b,c){this.fn=a,this.context=b,this.once=c||!1}function e(){}var f=Object.prototype.hasOwnProperty,g="function"!=typeof Object.create?"~":!1;e.prototype._events=void 0,e.prototype.eventNames=function(){var a,b=this._events,c=[];if(!b)return c;for(a in b)f.call(b,a)&&c.push(g?a.slice(1):a);return Object.getOwnPropertySymbols?c.concat(Object.getOwnPropertySymbols(b)):c},e.prototype.listeners=function(a,b){var c=g?g+a:a,d=this._events&&this._events[c];if(b)return!!d;if(!d)return[];if(d.fn)return[d.fn];for(var e=0,f=d.length,h=new Array(f);f>e;e++)h[e]=d[e].fn;return h},e.prototype.emit=function(a,b,c,d,e,f){var h=g?g+a:a;if(!this._events||!this._events[h])return!1;var i,j,k=this._events[h],l=arguments.length;if("function"==typeof k.fn){switch(k.once&&this.removeListener(a,k.fn,void 0,!0),l){case 1:return k.fn.call(k.context),!0;case 2:return k.fn.call(k.context,b),!0;case 3:return k.fn.call(k.context,b,c),!0;case 4:return k.fn.call(k.context,b,c,d),!0;case 5:return k.fn.call(k.context,b,c,d,e),!0;case 6:return k.fn.call(k.context,b,c,d,e,f),!0}for(j=1,i=new Array(l-1);l>j;j++)i[j-1]=arguments[j];k.fn.apply(k.context,i)}else{var m,n=k.length;for(j=0;n>j;j++)switch(k[j].once&&this.removeListener(a,k[j].fn,void 0,!0),l){case 1:k[j].fn.call(k[j].context);break;case 2:k[j].fn.call(k[j].context,b);break;case 3:k[j].fn.call(k[j].context,b,c);break;default:if(!i)for(m=1,i=new Array(l-1);l>m;m++)i[m-1]=arguments[m];k[j].fn.apply(k[j].context,i)}}return!0},e.prototype.on=function(a,b,c){var e=new d(b,c||this),f=g?g+a:a;return this._events||(this._events=g?{}:Object.create(null)),this._events[f]?this._events[f].fn?this._events[f]=[this._events[f],e]:this._events[f].push(e):this._events[f]=e,this},e.prototype.once=function(a,b,c){var e=new d(b,c||this,!0),f=g?g+a:a;return this._events||(this._events=g?{}:Object.create(null)),this._events[f]?this._events[f].fn?this._events[f]=[this._events[f],e]:this._events[f].push(e):this._events[f]=e,this},e.prototype.removeListener=function(a,b,c,d){var e=g?g+a:a;if(!this._events||!this._events[e])return this;var f=this._events[e],h=[];if(b)if(f.fn)(f.fn!==b||d&&!f.once||c&&f.context!==c)&&h.push(f);else for(var i=0,j=f.length;j>i;i++)(f[i].fn!==b||d&&!f[i].once||c&&f[i].context!==c)&&h.push(f[i]);return h.length?this._events[e]=1===h.length?h[0]:h:delete this._events[e],this},e.prototype.removeAllListeners=function(a){return this._events?(a?delete this._events[g?g+a:a]:this._events=g?{}:Object.create(null),this):this},e.prototype.off=e.prototype.removeListener,e.prototype.addListener=e.prototype.on,e.prototype.setMaxListeners=function(){return this},e.prefixed=g,"undefined"!=typeof b&&(b.exports=e)},{}],2:[function(a,b,c){"use strict";Object.defineProperty(c,"__esModule",{value:!0})},{}],3:[function(a,b,c){"use strict";function d(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:!0;h=a}function e(a){h&&i.push(a)}function f(a){h&&j.push(a)}function g(){for(;i.length;)i.pop();for(;j.length;)j.pop()}Object.defineProperty(c,"__esModule",{value:!0});var h=!1,i=[],j=[];c.useKeep=d,c.addStore=e,c.addAction=f,c.createdStores=i,c.createdActions=j,c.reset=g},{}],4:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}Object.defineProperty(c,"__esModule",{value:!0}),c.joinStrict=c.joinConcat=c.joinLeading=c.joinTrailing=c.fetchInitialState=c.stopListeningToAll=c.stopListeningTo=c.listenTo=c.validateListening=c.listenToMany=c.hasListener=void 0;var e=a("./utils"),f=d(e),g=a("./joins"),h=function(a){for(var b,c=0,d={};c<(a.children||[]).length;++c)b=a.children[c],a[b]&&(d[b]=a[b]);return d},i=function j(a){var b={};for(var c in a){var d=a[c],e=h(d),g=j(e);b[c]=d;for(var i in g){var k=g[i];b[c+f.capitalize(i)]=k}}return b};c.hasListener=function(a){for(var b,c,d,e=0;e<(this.subscriptions||[]).length;++e)for(d=[].concat(this.subscriptions[e].listenable),b=0;b<d.length;b++)if(c=d[b],c===a||c.hasListener&&c.hasListener(a))return!0;return!1},c.listenToMany=function(a){var b=i(a);for(var c in b){var d=f.callbackName(c),e=this[d]?d:this[c]?c:void 0;e&&this.listenTo(b[c],e,this[d+"Default"]||this[e+"Default"]||e)}},c.validateListening=function(a){return a===this?"Listener is not able to listen to itself":f.isFunction(a.listen)?a.hasListener&&a.hasListener(this)?"Listener cannot listen to this listenable because of circular loop":void 0:a+" is missing a listen method"},c.listenTo=function(a,b,c){var d,e,g,h=this.subscriptions=this.subscriptions||[];return f.throwIf(this.validateListening(a)),this.fetchInitialState(a,c),d=a.listen(this[b]||b,this),e=function(){var a=h.indexOf(g);f.throwIf(-1===a,"Tried to remove listen already gone from subscriptions list!"),h.splice(a,1),d()},g={stop:e,listenable:a},h.push(g),g},c.stopListeningTo=function(a){for(var b,c=0,d=this.subscriptions||[];c<d.length;c++)if(b=d[c],b.listenable===a)return b.stop(),f.throwIf(-1!==d.indexOf(b),"Failed to remove listen from subscriptions list!"),!0;return!1},c.stopListeningToAll=function(){for(var a,b=this.subscriptions||[];a=b.length;)b[0].stop(),f.throwIf(b.length!==a-1,"Failed to remove listen from subscriptions list!")},c.fetchInitialState=function(a,b){b=b&&this[b]||b;var c=this;if(f.isFunction(b)&&f.isFunction(a.getInitialState)){var d=a.getInitialState();d&&f.isFunction(d.then)?d.then(function(){b.apply(c,arguments)}):b.call(this,d)}},c.joinTrailing=(0,g.instanceJoinCreator)("last"),c.joinLeading=(0,g.instanceJoinCreator)("first"),c.joinConcat=(0,g.instanceJoinCreator)("all"),c.joinStrict=(0,g.instanceJoinCreator)("strict")},{"./joins":11,"./utils":13}],5:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}Object.defineProperty(c,"__esModule",{value:!0}),c.deferWith=c.triggerAsync=c.trigger=c.listen=c.shouldEmit=c.preEmit=void 0;var e=a("./utils"),f=d(e);c.preEmit=function(){},c.shouldEmit=function(){return!0},c.listen=function(a,b){b=b||this;var c=function(c){e||a.apply(b,c)},d=this,e=!1;return this.emitter.addListener(this.eventLabel,c),function(){e=!0,d.emitter.removeListener(d.eventLabel,c)}},c.trigger=function(){var a=arguments,b=this.preEmit.apply(this,a);a=void 0===b?a:f.isArguments(b)?b:[].concat(b),this.shouldEmit.apply(this,a)&&this.emitter.emit(this.eventLabel,a)},c.triggerAsync=function(){var a=arguments,b=this;f.nextTick(function(){b.trigger.apply(b,a)})},c.deferWith=function(a){var b=this.trigger,c=this,d=function(){b.apply(c,arguments)};this.trigger=function(){a.apply(c,[d].concat([].splice.call(arguments,0)))}}},{"./utils":13}],6:[function(a,b,c){arguments[4][2][0].apply(c,arguments)},{dup:2}],7:[function(a,b,c){"use strict";function d(a,b){for(var c in b)if(Object.getOwnPropertyDescriptor&&Object.defineProperty){var d=Object.getOwnPropertyDescriptor(b,c);if(!d.value||"function"!=typeof d.value||!b.hasOwnProperty(c))continue;a[c]=b[c].bind(a)}else{var e=b[c];if("function"!=typeof e||!b.hasOwnProperty(c))continue;a[c]=e.bind(a)}return a}Object.defineProperty(c,"__esModule",{value:!0}),c.bindMethods=d},{}],8:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){a=a||{},g.isObject(a)||(a={actionName:a});for(var b in i)if(!n[b]&&k[b])throw new Error("Cannot override API method "+b+" in Reflux.ActionMethods. Use another method name or override it on Reflux.PublisherMethods instead.");for(var c in a)if(!n[c]&&k[c])throw new Error("Cannot override API method "+c+" in action creation. Use another method name or override it on Reflux.PublisherMethods instead.");a.children=a.children||[],a.asyncResult&&(a.children=a.children.concat(["completed","failed"]));for(var d=0,f={};d<a.children.length;d++){var h=a.children[d],j="string"==typeof h?h:h.actionName;f[j]=e(h)}var l=g.extend({eventLabel:"action",emitter:new g.EventEmitter,_isAction:!0},k,i,a),o=function p(){var a=!1;for(var b in p.childActions){a=!0;break}var c=!p.sync&&"undefined"!=typeof p.sync||a,d=c?"triggerAsync":"trigger";return p[d].apply(p,arguments)};return g.extend(o,f,l),m.addAction(o),o}Object.defineProperty(c,"__esModule",{value:!0}),c.createAction=e;var f=a("./utils"),g=d(f),h=a("./ActionMethods"),i=d(h),j=a("./PublisherMethods"),k=d(j),l=a("./Keep"),m=d(l),n={preEmit:1,shouldEmit:1}},{"./ActionMethods":2,"./Keep":3,"./PublisherMethods":5,"./utils":13}],9:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){function b(){var b,c=0;if(this.subscriptions=[],this.emitter=new g.EventEmitter,this.eventLabel="change",(0,k.bindMethods)(this,a),this.init&&g.isFunction(this.init)&&this.init(),this.listenables)for(b=[].concat(this.listenables);c<b.length;c++)this.listenToMany(b[c])}a=a||{};for(var c in m)if(!r[c]&&(o[c]||q[c]))throw new Error("Cannot override API method "+c+" in Reflux.StoreMethods. Use another method name or override it on Reflux.PublisherMethods / Reflux.ListenerMethods instead.");for(var d in a)if(!r[d]&&(o[d]||q[d]))throw new Error("Cannot override API method "+d+" in store creation. Use another method name or override it on Reflux.PublisherMethods / Reflux.ListenerMethods instead.");a=(0,j.mix)(a),g.extend(b.prototype,q,o,m,a);var e=new b;return i.addStore(e),e}Object.defineProperty(c,"__esModule",{value:!0}),c.createStore=e;var f=a("./utils"),g=d(f),h=a("./Keep"),i=d(h),j=a("./mixer"),k=a("./bindMethods"),l=a("./StoreMethods"),m=d(l),n=a("./PublisherMethods"),o=d(n),p=a("./ListenerMethods"),q=d(p),r={preEmit:1,shouldEmit:1}},{"./Keep":3,"./ListenerMethods":4,"./PublisherMethods":5,"./StoreMethods":6,"./bindMethods":7,"./mixer":12,"./utils":13}],10:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){r.EventEmitter=a}function f(a){r.nextTick=a}function g(a){a(this)}Object.defineProperty(c,"__esModule",{value:!0}),c.__keep=c.joinConcat=c.joinStrict=c.joinLeading=c.all=c.joinTrailing=c.use=c.nextTick=c.setEventEmitter=c.createActions=c.createStore=c.createAction=c.utils=c.StoreMethods=c.PublisherMethods=c.ListenerMethods=c.ActionMethods=c.version=void 0;var h=a("./ActionMethods"),i=d(h),j=a("./ListenerMethods"),k=d(j),l=a("./PublisherMethods"),m=d(l),n=a("./StoreMethods"),o=d(n),p=a("./joins"),q=a("./utils"),r=d(q),s=a("./createAction"),t=a("./createStore"),u=a("./Keep"),v=d(u),w={"reflux-core":"1.0.0"},x=(0,p.staticJoinCreator)("last"),y=x,z=(0,p.staticJoinCreator)("first"),A=(0,p.staticJoinCreator)("strict"),B=(0,p.staticJoinCreator)("all"),C=r,D=function(){var a=function(a,b){Object.keys(a).forEach(function(c){var d=a[c];b[c]=(0,s.createAction)(d)})};return function(b){var c={};return b instanceof Array?b.forEach(function(b){r.isObject(b)?a(b,c):c[b]=(0,s.createAction)(b)}):a(b,c),c}}();c.version=w,c.ActionMethods=i,c.ListenerMethods=k,c.PublisherMethods=m,c.StoreMethods=o,c.utils=C,c.createAction=s.createAction,c.createStore=t.createStore,c.createActions=D,c.setEventEmitter=e,c.nextTick=f,c.use=g,c.joinTrailing=x,c.all=y,c.joinLeading=z,c.joinStrict=A,c.joinConcat=B,c.__keep=v,Object.defineProperty(c,"default",{get:function(){return c}}),Function.prototype.bind||console.error("Function.prototype.bind not available. ES5 shim required. https://github.com/spoike/refluxjs#es5")},{"./ActionMethods":2,"./Keep":3,"./ListenerMethods":4,"./PublisherMethods":5,"./StoreMethods":6,"./createAction":8,"./createStore":9,"./joins":11,"./utils":13}],11:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return function(){var b=n.call(arguments);return(0,k.createStore)({init:function(){this[o[a]].apply(this,b.concat("triggerAsync"))}})}}function f(a){return function(){m.throwIf(arguments.length<2,"Cannot create a join with less than 2 listenables!");var b,c,d=n.call(arguments),e=d.pop(),f=d.length,j={numberOfListenables:f,callback:this[e]||e,listener:this,strategy:a},k=[];for(b=0;f>b;b++)m.throwIf(this.validateListening(d[b]));for(b=0;f>b;b++)k.push(d[b].listen(i(b,j),this));return h(j),c={listenable:d},c.stop=g(c,k,this),this.subscriptions=(this.subscriptions||[]).concat(c),c}}function g(a,b,c){return function(){var d,e=c.subscriptions,f=e?e.indexOf(a):-1;for(m.throwIf(-1===f,"Tried to remove join already gone from subscriptions list!"),d=0;d<b.length;d++)b[d]();e.splice(f,1)}}function h(a){a.listenablesEmitted=new Array(a.numberOfListenables),a.args=new Array(a.numberOfListenables)}function i(a,b){return function(){var c=n.call(arguments);if(b.listenablesEmitted[a])switch(b.strategy){case"strict":throw new Error("Strict join failed because listener triggered twice.");case"last":b.args[a]=c;break;case"all":b.args[a].push(c)}else b.listenablesEmitted[a]=!0,b.args[a]="all"===b.strategy?[c]:c;j(b)}}function j(a){for(var b=0;b<a.numberOfListenables;b++)if(!a.listenablesEmitted[b])return;a.callback.apply(a.listener,a.args),h(a)}Object.defineProperty(c,"__esModule",{value:!0}),c.staticJoinCreator=e,c.instanceJoinCreator=f;var k=a("./createStore"),l=a("./utils"),m=d(l),n=Array.prototype.slice,o={strict:"joinStrict",first:"joinLeading",last:"joinTrailing",all:"joinConcat"}},{"./createStore":9,"./utils":13}],12:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){var b={init:[],preEmit:[],shouldEmit:[]},c=function d(a){var c={};return a.mixins&&a.mixins.forEach(function(a){g.extend(c,d(a))}),g.extend(c,a),Object.keys(b).forEach(function(c){a.hasOwnProperty(c)&&b[c].push(a[c])}),c}(a);return b.init.length>1&&(c.init=function(){var a=arguments;b.init.forEach(function(b){b.apply(this,a)},this)}),b.preEmit.length>1&&(c.preEmit=function(){return b.preEmit.reduce(function(a,b){var c=b.apply(this,a);return void 0===c?a:[c]}.bind(this),arguments)}),b.shouldEmit.length>1&&(c.shouldEmit=function(){var a=arguments;return!b.shouldEmit.some(function(b){return!b.apply(this,a)},this)}),Object.keys(b).forEach(function(a){1===b[a].length&&(c[a]=b[a][0])}),c}Object.defineProperty(c,"__esModule",{value:!0}),c.mix=e;var f=a("./utils"),g=d(f)},{"./utils":13}],13:[function(a,b,c){"use strict";function d(a){return a.charAt(0).toUpperCase()+a.slice(1)}function e(a,b){return b=b||"on",b+c.capitalize(a)}function f(a){var b="undefined"==typeof a?"undefined":m(a);return"function"===b||"object"===b&&!!a}function g(a){if(!f(a))return a;for(var b,c,d,e=1,g=arguments.length;g>e;e++){b=arguments[e],c=Object.keys(b);for(var h=0;h<c.length;h++)if(d=c[h],Object.getOwnPropertyDescriptor&&Object.defineProperty){var i=Object.getOwnPropertyDescriptor(b,d);Object.defineProperty(a,d,i)}else a[d]=b[d]}return a}function h(a){return"function"==typeof a}function i(a){setTimeout(a,0)}function j(a,b){for(var c={},d=0;d<a.length;d++)c[a[d]]=b[d];return c}function k(a){return"object"===("undefined"==typeof a?"undefined":m(a))&&"callee"in a&&"number"==typeof a.length}function l(a,b){if(a)throw Error(b||a)}Object.defineProperty(c,"__esModule",{value:!0});var m="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a};c.capitalize=d,c.callbackName=e,c.isObject=f,c.extend=g,c.isFunction=h,c.nextTick=i,c.object=j,c.isArguments=k,c.throwIf=l,c.EventEmitter=a("eventemitter3")},{eventemitter3:1}],14:[function(a,b,c){var d=a("reflux-core/lib/utils"),e=a("reflux-core/lib/ListenerMethods");b.exports=d.extend({componentWillUnmount:e.stopListeningToAll},e)},{"reflux-core/lib/ListenerMethods":4,"reflux-core/lib/utils":13}],15:[function(a,b,c){var d=a("reflux-core");if(d.defineReact=a("./defineReact"),"undefined"!=typeof React&&React)d.defineReact(React);else try{var e=a("react");d.defineReact(e)}catch(f){}},{"./defineReact":18,react:void 0,"reflux-core":10}],16:[function(a,b,c){var d=a("reflux-core/lib/ListenerMethods"),e=a("./ListenerMixin"),f=a("reflux-core/lib/utils");b.exports=function(a,b){return f.throwIf("undefined"==typeof b,"Reflux.connect() requires a key."),{getInitialState:function(){return f.isFunction(a.getInitialState)?f.object([b],[a.getInitialState()]):{}},componentDidMount:function(){var c=this;f.extend(c,d),this.listenTo(a,function(a){c.setState(f.object([b],[a]))})},componentWillUnmount:e.componentWillUnmount}}},{"./ListenerMixin":14,"reflux-core/lib/ListenerMethods":4,"reflux-core/lib/utils":13}],17:[function(a,b,c){var d=a("reflux-core/lib/ListenerMethods"),e=a("./ListenerMixin"),f=a("reflux-core/lib/utils");b.exports=function(a,b,c){return f.throwIf(f.isFunction(b),"Reflux.connectFilter() requires a key."),{getInitialState:function(){if(!f.isFunction(a.getInitialState))return{};var d=c.call(this,a.getInitialState());return"undefined"!=typeof d?f.object([b],[d]):{}},componentDidMount:function(){var e=this;f.extend(this,d),this.listenTo(a,function(a){var d=c.call(e,a);e.setState(f.object([b],[d]))})},componentWillUnmount:e.componentWillUnmount}}},{"./ListenerMixin":14,"reflux-core/lib/ListenerMethods":4,"reflux-core/lib/utils":13}],18:[function(a,b,c){function d(a,b,c){var k,l;try{h=a||h||React,l=c||h.Component}catch(m){return}if(h&&l&&(!j||c)){var n=function(a){l.call(this,a)};if(f(n,l),k=n.prototype,k.storeKeys=null,k.componentWillMount=function(){if(this.store&&(Array.isArray(this.stores)?this.stores.unshift(this.store):this.stores=[this.store]),this.stores){this.__storeunsubscribes__=this.__storeunsubscribes__||[];for(var a=this.setState.bind(this),b=function(b){var c=e(this.storeKeys,b);c&&a(c)}.bind(this),c=0,d=this.stores.length;d>c;c++){var f=this.stores[c];if("function"==typeof f){var g=f.id;if(f.singleton||(f.singleton=new f,g&&(i.stores[g]=f.singleton)),this.stores[c]=f=f.singleton,f.id=g,g&&i.GlobalState[g]){for(var h in i.GlobalState[g])f.state[h]=i.GlobalState[g][h];i.GlobalState[g]=f.state}else g&&(i.GlobalState[g]=f.state)}this.__storeunsubscribes__.push(f.listen(b));var j=e(this.storeKeys,f.state);j&&this.setState(j)}}this.__readytomap__=!0;var k=this.__delayedmaps__;if(k)for(var l=0,m=k.length;m>l;l++)k[l].func(k[l].state);this.__delayedmaps__=null},k.componentWillUnmount=function(){for(var a=0,b=this.__storeunsubscribes__.length;b>a;a++)this.__storeunsubscribes__[a]();this.__readytomap__=!1},k.mapStoreToState=function(a,b){function c(a){var c=b.call(d,a);if(c){var e=!1;for(var f in c){e=!0;break}e&&d.setState(c)}}"function"==typeof a&&(a=a.singleton?a.singleton:i.initStore(a));var d=this;this.__storeunsubscribes__=this.__storeunsubscribes__||[],this.__storeunsubscribes__.push(a.listen(c)),this.__readytomap__?c(a.state):(this.__delayedmaps__=this.__delayedmaps__||[],this.__delayedmaps__.push({func:c,state:a.state}))},n.extend=function(a){return d(null,null,a)},c)return n;i.Component=n;var o=function(){this.__store__=i.createStore(),this.state={};var a=this;for(var b in this.__store__)!function(b){Object.defineProperty(a,b,{get:function(){return a.__store__[b]},set:function(c){a.__store__[b]=c}})}(b)};k=o.prototype,Object.defineProperty(k,"listenables",{get:function(){return this.__listenables__},set:function(a){var b={};Array.isArray(a)?a.forEach(function(a){for(var c in a)b[c]=a[c]}):b=a,this.__listenables__=b,this.listenToMany(b)},enumerable:!0,configurable:!0}),k.setState=function(a){for(var b in a)this.state[b]=a[b];this.id&&(i.GlobalState[this.id]=this.state),this.trigger(a)},Object.defineProperty(o,"isES6Store",{get:function(){return!0},enumerable:!0,configurable:!0}),i.Store=o,i.GlobalState=i.GlobalState||{},i.stores={},i.getGlobalState=function(){return g(i.GlobalState)},i.setGlobalState=function(a){for(var b in a)i.stores[b]?i.stores[b].setState(a[b]):i.GlobalState[b]=a[b]},i.initializeGlobalStore=i.initStore=function(a){var b=a.id;if(a.singleton)return a.singleton;if(!b)return a.singleton=new a,a.singleton;var c=a.singleton=new a;if(i.stores[b]=c,c.id=b,i.GlobalState[b]){for(var d in i.GlobalState[b])c.state[d]=i.GlobalState[b][d];i.GlobalState[b]=c.state}else i.GlobalState[b]=c.state;return c},j=!0}}function e(a,b){if(!a)return b;for(var c=!1,d={},e=0,f=a.length;f>e;e++){var g=a[e];b.hasOwnProperty(g)&&(c=!0,d[g]=b[g])}return c?d:!1}function f(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);a.prototype=null===b?Object.create(b):(c.prototype=b.prototype,new c)}function g(a,b){if(null===a||"object"!=typeof a)return a;if(a.constructor!==Object&&a.constructor!==Array)return a;if(a.constructor===Date||a.constructor===RegExp||a.constructor===Function||a.constructor===String||a.constructor===Number||a.constructor===Boolean)return new a.constructor(a);b=b||new a.constructor;for(var c in a)b[c]="undefined"==typeof b[c]?g(a[c],null):b[c];return b}var h,i=a("reflux-core"),j=!1;b.exports=d},{"reflux-core":10}],19:[function(a,b,c){var d=a("reflux-core");d.connect=a("./connect"),d.connectFilter=a("./connectFilter"),d.ListenerMixin=a("./ListenerMixin"),d.listenTo=a("./listenTo"),d.listenToMany=a("./listenToMany"),a("./addES6"),b.exports=d},{"./ListenerMixin":14,"./addES6":15,"./connect":16,"./connectFilter":17,"./listenTo":20,"./listenToMany":21,"reflux-core":10}],20:[function(a,b,c){var d=a("reflux-core/lib/ListenerMethods");b.exports=function(a,b,c){return{componentDidMount:function(){for(var e in d)if(this[e]!==d[e]){if(this[e])throw"Can't have other property '"+e+"' when using Reflux.listenTo!";this[e]=d[e]}this.listenTo(a,b,c)},componentWillUnmount:d.stopListeningToAll}}},{"reflux-core/lib/ListenerMethods":4}],21:[function(a,b,c){var d=a("reflux-core/lib/ListenerMethods");b.exports=function(a){return{componentDidMount:function(){for(var b in d)if(this[b]!==d[b]){if(this[b])throw"Can't have other property '"+b+"' when using Reflux.listenToMany!";this[b]=d[b]}this.listenToMany(a)},componentWillUnmount:d.stopListeningToAll}}},{"reflux-core/lib/ListenerMethods":4}]},{},[19])(19)}); |
@@ -39,3 +39,3 @@ | ||
asyncResult: true, // <- true to make a shortcut to adding 'completed' and 'failed' children | ||
sync: false, // <- set the action to emit synchronously or asynchronously (async by default) | ||
sync: false, // <- set the action to emit synchronously or asynchronously (sync by default unless there are child actions) | ||
preEmit: function(){...}, // shortcut for setting preEmit method (covered later) | ||
@@ -71,4 +71,6 @@ shouldEmit: function(){...} // shortcut for setting shouldEmit method (covered later) | ||
As you've read, actions can simply be invoked via `myAction()`. But internally, if the action's `sync` is set to true then the action does `myAction.trigger()` and if not it does `myAction.triggerAsync()` (you may also call these manually). The important difference is that synchronous action calls must emit immediately. Asynchronous actions (which are default) emit on the next tick of the JS event loop, and may have things such as `children` child actions in their definitions which they can call. | ||
As you've read, actions can simply be invoked via `myAction()`. But internally, if the action's `sync` is set to true then the action does `myAction.trigger()` and if not it does `myAction.triggerAsync()` (you may also call these manually). The important difference is that synchronous action calls must emit immediately. Asynchronous actions emit on the next tick of the JS event loop, and may have things such as `children` child actions in their definitions which they can call. | ||
Actions are sync by default unless specifically defined otherwise, or unless the action has child actions within it. | ||
### Asynchronous Loading via Child Actions | ||
@@ -75,0 +77,0 @@ |
@@ -128,4 +128,21 @@ | ||
### componentWillMount and componentWillUnmount | ||
The `Reflux.Component` class utilizes both the `componentWillMount` and `componentWillUnmount` methods in the React lifecycle. This means that any extension you create that uses these methods will override those methods within the `Reflux.Component` class you're extending. If you run into this problem the solution is simple: just run the appropriate `super`. For example, in a `componentWillMount`: | ||
```javascript | ||
// ... | ||
componentWillMount() | ||
{ | ||
// ... your stuff ... | ||
super.componentWillMount(); | ||
} | ||
//... | ||
``` | ||
### More: | ||
Learn about [Reflux Stores](../stores/) and [Actions](../actions/), or go back to the overview of the [docs](../). |
{ | ||
"name": "reflux", | ||
"version": "5.0.4", | ||
"version": "6.0.0", | ||
"description": "A simple library for uni-directional dataflow application architecture inspired by ReactJS Flux", | ||
@@ -33,3 +33,3 @@ "main": "src/index.js", | ||
"eventemitter3": "^1.1.1", | ||
"reflux-core": "^0.4.2" | ||
"reflux-core": "^1.0.0" | ||
}, | ||
@@ -36,0 +36,0 @@ "devDependencies": { |
1017
README.md
@@ -0,1 +1,2 @@ | ||
# RefluxJS | ||
@@ -6,145 +7,81 @@ | ||
[![NPM Version][npm-image]][npm-url] | ||
[![NPM Downloads][downloads-image]][npm-url] | ||
[![Bower Version][bower-image]][bower-url] | ||
[![Dependencies][dependencies-image]][npm-url] | ||
[![Build Status][travis-image]][travis-url] | ||
[![Gratipay][gratipay-image]][gratipay-url] | ||
[![NPM Downloads][downloads-image]][npm-url] | ||
[![Sauce Test Status](https://saucelabs.com/browser-matrix/refluxjs.svg)](https://saucelabs.com/u/refluxjs) | ||
You can read an overview of Flux [here](https://facebook.github.io/flux/docs/overview.html), however the gist of it is to introduce a more functional programming style architecture by eschewing MVC like pattern and adopting a single data flow pattern. | ||
-------------------- | ||
``` | ||
╔═════════╗ ╔════════╗ ╔═════════════════╗ | ||
║ Actions ║──────>║ Stores ║──────>║ View Components ║ | ||
╚═════════╝ ╚════════╝ ╚═════════════════╝ | ||
^ │ | ||
└──────────────────────────────────────┘ | ||
## Installation | ||
``` | ||
You can currently install the package as a npm package, a bower component, or import it from a CDN. | ||
The pattern is composed of actions and data stores, where actions initiate new data to pass through data stores before coming back to the view components again. If a view component has an event that needs to make a change in the application's data stores, they need to do so by signaling to the stores through the actions available. | ||
#### NPM | ||
Feel free to open an issue on our [**discussion forum**](https://github.com/reflux/discuss) for **questions and general discussion**. Here is a complete list of communication channels: | ||
The following command installs RefluxJS as a npm package: | ||
1. The [discussion forum](https://github.com/reflux/discuss) | ||
2. [StackOverflow with the `refluxjs` tag](http://stackoverflow.com/questions/tagged/refluxjs) | ||
3. `#flux` channel on Reactiflux Discord group. [Sign up here](https://discord.gg/0ZcbPKXt5bYedEbN) for an account. | ||
4. [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spoike/refluxjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) | ||
5. [![Thinkful][thinkful-image]][thinkful-url] | ||
npm install reflux | ||
Please use the [issue tracker](https://github.com/spoike/refluxjs/issues) only for bugs and feature requests. | ||
Then, in your script, you can gain a reference to RefluxJS like so: `var Reflux = require('reflux');` | ||
If you don't want to use the React-specific API, or want to develop Reflux for your view engine framework of choice, have a look at [`reflux-core`](https://github.com/reflux/reflux-core). | ||
#### Bower | ||
## Content | ||
The following command installs reflux as a bower component that can be used in the browser: | ||
- [Comparing RefluxJS with Facebook Flux](#comparing-refluxjs-with-facebook-flux) | ||
- [Examples](#examples) | ||
- [Extensions and Plugins](#extensions-and-plugins) | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [Actions](#creating-actions) | ||
- [Stores](#creating-data-stores) | ||
- [Component](#react-component-example) | ||
- [Advanced Usage](#advanced-usage) | ||
- [React ES6 Usage](#react-es6-usage) | ||
- [Colophon](#colophon) | ||
bower install reflux | ||
Then the files may be imported into your html file via `bower_components/reflux/dist/reflux.js` or `bower_components/reflux/dist/reflux.min.js`. At that point a `Reflux` variable will be globally available to you. It is suggested that you import RefluxJS after React. | ||
## Comparing RefluxJS with Facebook Flux | ||
#### CDN | ||
The goal of the refluxjs project is to get this architecture easily up and running in your web application, both client-side or server-side. There are some differences between how this project works and how Facebook's proposed Flux architecture works: | ||
RefluxJS is available at [jsdelivr](http://www.jsdelivr.com/#!refluxjs). | ||
You can read more in this [blog post about React Flux vs Reflux](http://spoike.ghost.io/deconstructing-reactjss-flux/). | ||
You may import the CDN files directly through a script tag. At that point a `Reflux` variable will be globally available to you. It is suggested that you import RefluxJS after React. | ||
### Similarities with Flux | ||
-------------------------------- | ||
Some concepts are still in Reflux in comparison with Flux: | ||
## Overview | ||
* There are actions | ||
* There are data stores | ||
* The data flow is unidirectional | ||
The main function of Reflux is to introduce a more functional programming style architecture by eschewing MVC like pattern and adopting a single data flow pattern. | ||
### Differences with Flux | ||
``` | ||
+---------+ +--------+ +-----------------+ | ||
¦ Actions ¦------>¦ Stores ¦------>¦ View Components ¦ | ||
+---------+ +--------+ +-----------------+ | ||
^ ¦ | ||
+--------------------------------------+ | ||
Reflux has refactored Flux to be a bit more dynamic and be more Functional Reactive Programming (FRP) friendly: | ||
``` | ||
* The singleton dispatcher is removed in favor for letting every action act as dispatcher instead. | ||
* Because actions are listenable, the stores may listen to them. Stores don't need to have big switch statements that do static type checking (of action types) with strings | ||
* Stores may listen to other stores, i.e. it is possible to create stores that can *aggregate data further*, similar to a map/reduce. | ||
* `waitFor` is replaced in favor to handle *serial* and *parallel* data flows: | ||
* **Aggregate data stores** (mentioned above) may listen to other stores in *serial* | ||
* **Joins** for joining listeners in *parallel* | ||
* *Action creators* are not needed because RefluxJS actions are functions that will pass on the payload they receive to anyone listening to them | ||
The pattern is composed of actions and data stores, where actions initiate new data to pass through data stores before coming back to the view components again. If a view component has an event that needs to make a change in the application's data stores, they need to do so by signaling to the stores through the actions available. | ||
[Back to top](#content) | ||
-------------------------------- | ||
## Examples | ||
## Usage | ||
You can find some example projects at these locations: | ||
For usage, you need to create actions which can be called from React components. Those actions are listened to by stores which hold and update data. In turn those stores are hooked up to React components and set state within them as it is updated within the store. | ||
* [Todo Example Project](https://github.com/spoike/refluxjs-todo) | ||
* [Hacker News Clone](https://github.com/echenley/react-news) by echenley | ||
* [Another Todo Project with a Python backend](https://github.com/limelights/todo-reflux) by limelights | ||
* [Sample app with authentication, permissions, sidebar and editable collection](https://github.com/VladimirPal/react-flux-backbone) | ||
* [TodoMVC demonstrating Reflux + Angular](https://github.com/javamonn/Angular-TodoMVC-Redux) | ||
* [Sample blog by @akornatskyy](https://github.com/akornatskyy/sample-blog-react) | ||
Therefore the 3 main concepts to know are: | ||
[Back to top](#content) | ||
1. [creating actions](#creating-actions) | ||
2. [creating stores](#creating-stores) | ||
3. [hooking stores to React components](#hooking-stores-to-components) | ||
## Extensions and Plugins | ||
-------------------------------- | ||
* [Cartiv](https://github.com/yonatanmn/cartiv) - new reactive interface based on Reflux | ||
* [reflux-promise](https://github.com/reflux/reflux-promise) - Extends reflux with Promises | ||
* [reflux-triggerable-mixin](https://github.com/jesstelford/reflux-triggerable-mixin) - Stores mixin adding `triggerable` syntax similar to `listenable` | ||
* [reflux-state-mixin](https://github.com/yonatanmn/reflux-state-mixin) - Stores mixin adding `state` syntax similar to React components. | ||
## Creating Actions | ||
## Installation | ||
You can currently install the package as a npm package or bower component. | ||
### NPM | ||
The following command installs reflux as a npm package: | ||
npm install reflux | ||
### Bower | ||
The following command installs reflux as a bower component that can be used in the browser: | ||
bower install reflux | ||
Then the files may be embedded in your html file via `bower_components/reflux/dist/reflux.js` or `bower_components/reflux/dist/reflux.min.js`. | ||
### CDN | ||
Reflux is available at [jsdelivr](http://www.jsdelivr.com/#!refluxjs). | ||
jsdelivr [v5.0.1](https://cdn.jsdelivr.net/refluxjs/5.0.1/reflux.min.js) | ||
### ES5 | ||
Like React, Reflux depends on an es5-shim for older browsers. The es5-shim.js from [kriskowal's es5-shim](https://github.com/kriskowal/es5-shim) provides everything required. | ||
[Back to top](#content) | ||
## Usage | ||
### Creating actions | ||
Create an action by calling `Reflux.createAction` with an optional options object. | ||
```javascript | ||
var statusUpdate = Reflux.createAction(options); | ||
var statusUpdate = Reflux.createAction(); | ||
``` | ||
An action is a [function object](http://en.wikipedia.org/wiki/Function_object) that can be invoked like any function. | ||
An action is a [function object](http://en.wikipedia.org/wiki/Function_object) that can be invoked like any other function. | ||
```javascript | ||
statusUpdate(data); // Invokes the action statusUpdate | ||
statusUpdate.triggerAsync(data); // same effect as above | ||
``` | ||
If `options.sync` is true, the functor will instead call `action.trigger` which is a synchronous operation. You can change `action.sync` during the lifetime of the action, and the following calls will honour that change. | ||
There is also a convenience function for creating multiple actions. | ||
@@ -157,3 +94,3 @@ | ||
"statusAdded" | ||
]); | ||
]); | ||
@@ -167,550 +104,30 @@ // Actions object now contains the actions | ||
#### Asynchronous actions | ||
#### More on Actions: | ||
Actions can also: | ||
- load files asychronously with child actions | ||
- do preEmit and shouldEmit checking | ||
- have many shortcuts for easy usage | ||
For actions that represent asynchronous operations (e.g. API calls), a few separate dataflows result from the operation. In the most typical case, we consider completion and failure of the operation. To create related actions for these dataflows, which you can then access as attributes, use `options.children`. | ||
See [Reflux Action Documentation](docs/actions/) for more. | ||
```javascript | ||
// this creates 'load', 'load.completed' and 'load.failed' | ||
var Actions = Reflux.createActions({ | ||
"load": {children: ["completed","failed"]} | ||
}); | ||
------------------ | ||
// when 'load' is triggered, call async operation and trigger related actions | ||
Actions.load.listen( function() { | ||
// By default, the listener is bound to the action | ||
// so we can access child actions using 'this' | ||
someAsyncOperation() | ||
.then( this.completed ) | ||
.catch( this.failed ); | ||
}); | ||
``` | ||
## Creating Stores | ||
There is a shorthand to define the `completed` and `failed` actions in the typical case: `options.asyncResult`. The following are equivalent: | ||
Create a data store much like ReactJS's own `React.Component` by creating a class extending `Reflux.Store`. The store has a `state` property much like a component, and uses `setState` like a component as well. You may set up all action listeners in the `constructor` and register them by calling the store's own `listenTo` function. | ||
```javascript | ||
createAction({ | ||
children: ["progressed","completed","failed"] | ||
}); | ||
createAction({ | ||
asyncResult: true, | ||
children: ["progressed"] | ||
}); | ||
``` | ||
#### Action hooks | ||
There are a couple of hooks available for each action. | ||
* `preEmit` - Is called before the action emits an event. It receives the arguments from the action invocation. If it returns something other than undefined, that will be used as arguments for `shouldEmit` and subsequent emission. | ||
* `shouldEmit` - Is called after `preEmit` and before the action emits an event. By default it returns `true` which will let the action emit the event. You may override this if you need to check the arguments that the action receives and see if it needs to emit the event. | ||
Example usage: | ||
```javascript | ||
Actions.statusUpdate.preEmit = function() { console.log(arguments); }; | ||
Actions.statusUpdate.shouldEmit = function(value) { | ||
return value > 0; | ||
}; | ||
Actions.statusUpdate(0); | ||
Actions.statusUpdate(1); | ||
// Should output: 1 | ||
``` | ||
You can also set the hooks by sending them in a definition object as you create the action: | ||
```javascript | ||
var action = Reflux.createAction({ | ||
preEmit: function(){...}, | ||
shouldEmit: function(){...} | ||
}); | ||
``` | ||
#### Reflux.ActionMethods | ||
If you would like to have a common set of methods available to all actions you can extend the `Reflux.ActionMethods` object, which is mixed into the actions when they are created. | ||
Example usage: | ||
```javascript | ||
Reflux.ActionMethods.exampleMethod = function() { console.log(arguments); }; | ||
Actions.statusUpdate.exampleMethod('arg1'); | ||
// Should output: 'arg1' | ||
``` | ||
[Back to top](#content) | ||
### Creating data stores | ||
Create a data store much like ReactJS's own `React.createClass` by passing a definition object to `Reflux.createStore`. You may set up all action listeners in the `init` function and register them by calling the store's own `listenTo` function. | ||
```javascript | ||
// Creates a DataStore | ||
var statusStore = Reflux.createStore({ | ||
// Initial setup | ||
init: function() { | ||
// Register statusUpdate action | ||
this.listenTo(statusUpdate, this.output); | ||
}, | ||
// Callback | ||
output: function(flag) { | ||
var status = flag ? 'ONLINE' : 'OFFLINE'; | ||
// Pass on to listeners | ||
this.trigger(status); | ||
} | ||
}); | ||
``` | ||
In the above example, whenever the action is called, the store's `output` callback will be called with whatever parameters were sent in the action. E.g. if the action is called as `statusUpdate(true)` then the flag argument in `output` function is `true`. | ||
A data store is a publisher much like the actions, so they too have the `preEmit` and `shouldEmit` hooks. | ||
#### Reflux.StoreMethods | ||
If you would like to have a common set of methods available to all stores you can extend the `Reflux.StoreMethods` object, which is mixed into the stores when they are created. | ||
Example usage: | ||
```javascript | ||
Reflux.StoreMethods.exampleMethod = function() { console.log(arguments); }; | ||
statusStore.exampleMethod('arg1'); | ||
// Should output: 'arg1' | ||
``` | ||
#### Mixins in stores | ||
Just as you can add mixins to React components, so it is possible to add your mixins to Store. | ||
```javascript | ||
var MyMixin = { foo: function() { console.log('bar!'); } } | ||
var Store = Reflux.createStore({ | ||
mixins: [MyMixin] | ||
}); | ||
Store.foo(); // outputs "bar!" to console | ||
``` | ||
Methods from mixins are available as well as the methods declared in the Store. So it's possible to access store's `this` from mixin, or methods of mixin from methods of store: | ||
```javascript | ||
var MyMixin = { mixinMethod: function() { console.log(this.foo); } } | ||
var Store = Reflux.createStore({ | ||
mixins: [MyMixin], | ||
foo: 'bar!', | ||
storeMethod: function() { | ||
this.mixinMethod(); // outputs "bar!" to console | ||
} | ||
}); | ||
``` | ||
A nice feature of mixins is that if a store is using multiple mixins and several mixins define the same lifecycle method (e.g. `init`, `preEmit`, `shouldEmit`), all of the lifecycle methods are guaranteed to be called. | ||
#### Listening to many actions at once | ||
Since it is a very common pattern to listen to all actions from a `createActions` call in a store `init` call, the store has a `listenToMany` function that takes an object of listenables. Instead of doing this: | ||
```javascript | ||
var actions = Reflux.createActions(["fireBall","magicMissile"]); | ||
var Store = Reflux.createStore({ | ||
init: function() { | ||
this.listenTo(actions.fireBall,this.onFireBall); | ||
this.listenTo(actions.magicMissile,this.onMagicMissile); | ||
}, | ||
onFireBall: function(){ | ||
// whoooosh! | ||
}, | ||
onMagicMissile: function(){ | ||
// bzzzzapp! | ||
} | ||
}); | ||
``` | ||
...you can do this: | ||
```javascript | ||
var actions = Reflux.createActions(["fireBall","magicMissile"]); | ||
var Store = Reflux.createStore({ | ||
init: function() { | ||
this.listenToMany(actions); | ||
}, | ||
onFireBall: function(){ | ||
// whoooosh! | ||
}, | ||
onMagicMissile: function(){ | ||
// bzzzzapp! | ||
} | ||
}); | ||
``` | ||
This will add listeners to all actions `actionName` who have a corresponding `onActionName` (or `actionName` if you prefer) method in the store. Thus if the `actions` object should also have included an `iceShard` spell, that would simply be ignored. | ||
#### The listenables shorthand | ||
To make things more convenient still, if you give an object of actions to the `listenables` property of the store definition, that will be automatically passed to `listenToMany`. So the above example can be simplified even further: | ||
```javascript | ||
var actions = Reflux.createActions(["fireBall","magicMissile"]); | ||
var Store = Reflux.createStore({ | ||
listenables: actions, | ||
onFireBall: function(){ | ||
// whoooosh! | ||
}, | ||
onMagicMissile: function(){ | ||
// bzzzzapp! | ||
} | ||
}); | ||
``` | ||
The `listenables` property can also be an array of such objects, in which case all of them will be sent to `listenToMany`. This allows you to do convenient things like this: | ||
```javascript | ||
var Store = Reflux.createStore({ | ||
listenables: [require('./darkspells'),require('./lightspells'),{healthChange:require('./healthstore')}], | ||
// rest redacted | ||
}); | ||
``` | ||
#### Listenables and asynchronous actions | ||
If `options.children` is set, as in the example below, you can use `onActionSubaction` to add a listener to the child action. For example: | ||
```javascript | ||
var Actions = Reflux.createActions({ | ||
"load": {children: ["completed", "failed"]} | ||
}); | ||
function handleLoad(Action, Subaction){ | ||
console.log("The on" + Action + Subaction + " handler was called"); | ||
}; | ||
var Store = Reflux.createStore({ | ||
listenables: Actions, | ||
onLoad: function() { | ||
handleLoad("Load"); | ||
}, | ||
onLoadCompleted: function() { | ||
handleLoad("Load", "Completed"); | ||
}, | ||
onLoadFailed: function() { | ||
handleLoad("Load", "Failed"); | ||
} | ||
}); | ||
``` | ||
### Listening to changes in data store | ||
In your component, register to listen to changes in your data store like this: | ||
```javascript | ||
// Fairly simple view component that outputs to console | ||
function ConsoleComponent() { | ||
// Registers a console logging callback to the statusStore updates | ||
statusStore.listen(function(status) { | ||
console.log('status: ', status); | ||
}); | ||
}; | ||
var consoleComponent = new ConsoleComponent(); | ||
``` | ||
Invoke actions as if they were functions: | ||
```javascript | ||
statusUpdate(true); | ||
statusUpdate(false); | ||
``` | ||
With the setup above this will output the following in the console: | ||
``` | ||
status: ONLINE | ||
status: OFFLINE | ||
``` | ||
[Back to top](#content) | ||
### React component example | ||
Register your component to listen for changes in your data stores, preferably in the `componentDidMount` [lifecycle method](http://facebook.github.io/react/docs/component-specs.html) and unregister in the `componentWillUnmount`, like this: | ||
```javascript | ||
var Status = React.createClass({ | ||
getInitialState: function() { }, | ||
onStatusChange: function(status) { | ||
this.setState({ | ||
currentStatus: status | ||
}); | ||
}, | ||
componentDidMount: function() { | ||
this.unsubscribe = statusStore.listen(this.onStatusChange); | ||
}, | ||
componentWillUnmount: function() { | ||
this.unsubscribe(); | ||
}, | ||
render: function() { | ||
// render specifics | ||
} | ||
}); | ||
``` | ||
It's also important to note that Reflux now supports [React ES6 style usage](#react-es6-usage) as well. | ||
#### Convenience mixin for React | ||
You always need to unsubscribe components from observed actions and stores upon | ||
unmounting. To simplify this process you can use [mixins in React](http://facebook.github.io/react/docs/reusable-components.html#mixins). There is a convenience mixin available at `Reflux.ListenerMixin`. Using that, the above example can be written like thus: | ||
```javascript | ||
var Status = React.createClass({ | ||
mixins: [Reflux.ListenerMixin], | ||
onStatusChange: function(status) { | ||
this.setState({ | ||
currentStatus: status | ||
}); | ||
}, | ||
componentDidMount: function() { | ||
this.listenTo(statusStore, this.onStatusChange); | ||
}, | ||
render: function() { | ||
// render specifics | ||
} | ||
}); | ||
``` | ||
The mixin provides the `listenTo` method for the React component, that works much like the one found in the Reflux's stores, and handles the listeners during mount and unmount for you. You also get the same `listenToMany` method as the store has. | ||
#### Using Reflux.listenTo | ||
If you're not reliant on any special logic for the `this.listenTo` calls inside `componentDidMount`, you can instead use a call to `Reflux.listenTo` as a mixin. That will automatically set up the `componentDidMount` and the rest for you, as well as add the `ListenerMixin` functionality. With this our example above can be reduced even further: | ||
```javascript | ||
var Status = React.createClass({ | ||
mixins: [Reflux.listenTo(statusStore,"onStatusChange")], | ||
onStatusChange: function(status) { | ||
this.setState({ | ||
currentStatus: status | ||
}); | ||
}, | ||
render: function() { | ||
// render using `this.state.currentStatus` | ||
} | ||
}); | ||
``` | ||
You can have multiple calls to `Reflux.listenTo` in the same `mixins` array. | ||
There is also `Reflux.listenToMany` which works in exactly the same way, exposing `listener.listenToMany`. | ||
#### Using Reflux.connect | ||
If all you want to do is update the state of your component to whatever the data store transmits, you can use `Reflux.connect(listener,stateKey)` as a mixin. The state is updated via `this.setState({<stateKey>:data})`. Here's the example above changed to use this syntax: | ||
```javascript | ||
var Status = React.createClass({ | ||
mixins: [Reflux.connect(statusStore,"currentStatus")], | ||
render: function() { | ||
// render using `this.state.currentStatus` | ||
} | ||
}); | ||
``` | ||
The `Reflux.connect()` mixin will check the store for a `getInitialState` method. If found it will set the components `getInitialState` | ||
```javascript | ||
var statusStore = Reflux.createStore({ | ||
getInitialState: function() { | ||
return "open"; | ||
} | ||
}); | ||
var Status = React.createClass({ | ||
mixins: [Reflux.connect(statusStore,"currentStatus")], | ||
render: function() { | ||
// render using `this.state.currentStatus` | ||
// this.state.currentStatus === "open" | ||
} | ||
}); | ||
``` | ||
#### Using Reflux.connectFilter | ||
`Reflux.connectFilter` is used in a similar manner to `Reflux.connect`. Use the | ||
`connectFilter` mixin when you want only a subset of the items in a store. A | ||
blog written using Reflux would probably have a store with all posts in | ||
it. For an individual post page, you could use `Reflux.connectFilter` to | ||
filter the posts to the post that's being viewed. | ||
```javascript | ||
var PostView = React.createClass({ | ||
mixins: [Reflux.connectFilter(postStore, "post", function(posts) { | ||
return posts.filter(function(post) { | ||
return post.id === this.props.id; | ||
}.bind(this))[0]; | ||
})], | ||
render: function() { | ||
// render using `this.state.post` | ||
} | ||
}); | ||
``` | ||
### Listening to changes in other data stores (aggregate data stores) | ||
A store may listen to another store's change, making it possible to safely chain stores for aggregated data without affecting other parts of the application. A store may listen to other stores using the same `listenTo` function as with actions: | ||
```javascript | ||
// Creates a DataStore that listens to statusStore | ||
var statusHistoryStore = Reflux.createStore({ | ||
init: function() { | ||
// Register statusStore's changes | ||
this.listenTo(statusStore, this.output); | ||
this.history = []; | ||
}, | ||
// Callback | ||
output: function(statusString) { | ||
this.history.push({ | ||
date: new Date(), | ||
status: statusString | ||
}); | ||
// Pass the data on to listeners | ||
this.trigger(this.history); | ||
} | ||
}); | ||
``` | ||
[Back to top](#content) | ||
## Advanced usage | ||
### Switching EventEmitter | ||
Don't like to use the EventEmitter provided? You can switch to another one, such as NodeJS's own like this: | ||
```javascript | ||
// Do this before creating actions or stores | ||
Reflux.setEventEmitter(require('events').EventEmitter); | ||
``` | ||
### Switching nextTick | ||
Whenever action functors are called, they return immediately through the use of `setTimeout` (`nextTick` function) internally. | ||
You may switch out for your favorite `setTimeout`, `nextTick`, `setImmediate`, et al implementation: | ||
```javascript | ||
// node.js env | ||
Reflux.nextTick(process.nextTick); | ||
``` | ||
For better alternative to `setTimeout`, you may opt to use the [`setImmediate` polyfill](https://github.com/YuzuJS/setImmediate), [`setImmediate2`](https://github.com/Katochimoto/setImmediate) or [`macrotask`](https://github.com/calvinmetcalf/macrotask). | ||
### Joining parallel listeners with composed listenables | ||
The Reflux API contains `join` methods that makes it easy to aggregate publishers that emit events in parallel. This corresponds to the `waitFor` method in Flux. | ||
#### Argument tracking | ||
A join is triggered once all participating publishers have emitted at least once. The callback will be called with the data from the various emissions, in the same order as the publishers were listed when the join was created. | ||
There are four join methods, each representing a different strategy to track the emission data: | ||
* `joinLeading`: Only the first emission from each publisher is saved. Subsequent emissions by the same publisher before all others are finished are ignored. | ||
* `joinTrailing`: If a publisher triggers twice, the second emission overwrites the first. | ||
* `joinConcat`: An array of emission arguments are stored for each publisher. | ||
* `joinStrict`: An error is thrown if a publisher emits twice before the join is completed. | ||
The method signatures all look like this: | ||
```javascript | ||
joinXyz(...publisher,callback) | ||
``` | ||
Once a join is triggered it will reset, and thus it can trigger again when all publishers have emitted anew. | ||
#### Using the listener instance methods | ||
All objects using the listener API (stores, React components using `ListenerMixin`, or other components using the `ListenerMethods`) gain access to the four join instance methods, named after the argument strategy. Here's an example saving the last emission from each publisher: | ||
```javascript | ||
var gainHeroBadgeStore = Reflux.createStore({ | ||
init: function() { | ||
this.joinTrailing(actions.disarmBomb, actions.saveHostage, actions.recoverData, this.triggerAsync); | ||
} | ||
}); | ||
actions.disarmBomb("warehouse"); | ||
actions.recoverData("seedyletter"); | ||
actions.disarmBomb("docks"); | ||
actions.saveHostage("offices",3); | ||
// `gainHeroBadgeStore` will now asynchronously trigger `[["docks"],["offices",3],["seedyletter"]]`. | ||
``` | ||
#### Using the static methods | ||
Since it is rather common to have a store where the only purpose is to listen to a join and trigger when the join is completed, the join methods have static counterparts on the `Reflux` object which return stores listening to the requested join. Using them, the store in the example above could instead be created like this: | ||
```javascript | ||
var gainHeroBadgeStore = Reflux.joinTrailing(actions.disarmBomb, actions.saveHostage, actions.recoverData); | ||
``` | ||
### Sending initial state with the listenTo function | ||
The `listenTo` function provided by the `Store` and the `ListenerMixin` has a third parameter that accepts a callback. This callback will be invoked when the listener is registered with whatever the `getInitialState` is returning. | ||
```javascript | ||
var exampleStore = Reflux.createStore({ | ||
init: function() {}, | ||
getInitialState: function() { | ||
return "the initial data"; | ||
} | ||
}); | ||
// Anything that will listen to the example store | ||
this.listenTo(exampleStore, onChangeCallback, initialCallback) | ||
// initialCallback will be invoked immediately with "the initial data" as first argument | ||
``` | ||
Remember the `listenToMany` method? In case you use that with other stores, it supports `getInitialState`. That data is sent to the normal listening callback, or a `this.on<Listenablename>Default` method if that exists. | ||
[Back to top](#content) | ||
## React ES6 Usage | ||
### React ES6 component example | ||
Reflux exposes `Reflux.Component` for class extension for easy creation of ES6 style React components that automatically has the state of one or more Reflux stores mixed into the React component state. In order to accomplish this you simply need use Reflux stores that start with a `state` property with an object holding the default state of the store's data (i.e. set `this.state = {my:"defaults"}` in the store's `init`) , then you need to set `this.store` (to 1 store) or `this.stores` (to an Array of stores) from within the constructor of the component. An example would look like this: | ||
```javascript | ||
class MyComponent extends Reflux.Component // <- Reflux.Component instead of React.Component | ||
class StatusStore extends Reflux.Store | ||
{ | ||
constructor(props) { | ||
super(props); | ||
this.state = {foo:'bar'}; // <- stays usable, so normal state usage can still happen | ||
this.store = myStore; // <- the only thing needed to tie the store into this component | ||
constructor() | ||
{ | ||
super(); | ||
this.state = {flag:'OFFLINE'}; // <- set store's default state much like in React | ||
this.listenTo(statusUpdate, this.onStatusUpdate); // listen to the statusUpdate action | ||
} | ||
render() { | ||
// `storeProp` is mixed in from the store, and reflects in the component state | ||
return <p>From Store: {this.state.storeProp}, Foo: {this.state.foo}</p>; | ||
onStatusUpdate(status) | ||
{ | ||
var newFlag = status ? 'ONLINE' : 'OFFLINE'; | ||
this.setState({flag:newFlag}); | ||
} | ||
@@ -720,193 +137,57 @@ } | ||
The default states of the stores will be mixed in from the start, and any time the store does a `trigger` the triggered data will be mixed in to the component and it will re-render. | ||
In the above example, whenever the action `statusUpdate` is called, the store's `onStatusUpdate` callback will be called with whatever parameters were sent in the action. E.g. if the action is called as `statusUpdate(true)` then the `status` argument in the `onStatusUpdate` function is `true`. | ||
A fully working example may look something like this: | ||
Stores also integrate easily with sets of actions via things like `this.listenables`. When an actions object (or an Array of multiple actions objects) is applied to `this.listenables` you may automatically add listeners simply by naming convention. Just name the functions either after the action name (such as `actionName`, or the camelcased action name preceded with "on", (such as `onActionName`). | ||
```javascript | ||
var Actions = Reflux.createActions(["increment"]); | ||
var Actions = Reflux.createActions(['firstAction', 'secondAction']); | ||
var counterStore = Reflux.createStore( | ||
class StatusStore extends Reflux.Store | ||
{ | ||
listenables: Actions, | ||
init: function() { | ||
this.state = {count:0}; | ||
}, | ||
onIncrement: function(txt) { | ||
this.state.count++; | ||
this.trigger(this.state); | ||
} | ||
}); | ||
class Counter extends Reflux.Component | ||
{ | ||
constructor(props) { | ||
super(props); | ||
this.state = {}; | ||
this.store = counterStore; | ||
} | ||
render() { | ||
return <p>Count: {this.state.count}</p>; | ||
} | ||
} | ||
ReactDOM.render( | ||
<Counter />, | ||
document.getElementById('container') | ||
); | ||
setInterval(Actions.increment, 1000); | ||
``` | ||
NOTE: If you are also using `Reflux.Store` ES6 stores and updating them properly via their `setState` method then you may also choose to only mix in certain properties from the store(s) attached to a component, instead of all of them. To do this you may define `this.storeKeys` in the component's constructor and set it to an array of key names (strings) for properties you want mixed in from the attached stores. The component will then only mix in state object properties of those key names for any stores attached to it. If the store state is changed and none of the changed state involves the keys in `this.storeKeys` then the component will not change state at all nor re-render. | ||
```javascript | ||
//... | ||
constructor(props) { | ||
super(props); | ||
this.state = {}; | ||
this.store = MyStore; | ||
this.storeKeys = ['color', 'height']; | ||
// ^ will only include the color and height parts of MyStore's state | ||
} | ||
//... | ||
``` | ||
### Using ES6 Reflux Stores via Reflux.Store | ||
Stores do not directly integrate within React like `Reflux.Component` needs to, so using a more idiomatic way to declare them is not necessary. However, it can be very useful when using the `Reflux.Component` style components. Therefore whenever `Reflux.Component` is exposed Reflux also exposes `Reflux.Store` which can be extended to make a class that wraps and acts as a reflux store but with an approach that is easier to implement into `Reflux.Component` classes. | ||
To create one looks something like this: | ||
```javascript | ||
class MyStore extends Reflux.Store | ||
{ | ||
constructor() { | ||
constructor() | ||
{ | ||
super(); | ||
this.state = {foo:'bar'}; // <-- the store's default state | ||
} | ||
} | ||
``` | ||
These act much like a normal store. You can use `this.listenTo`, `this.listenToMany`, etc. from within the constructor, and you can define things like a `this.listenables` property and it will automatically call `action` and `onAction` named methods on the class. It also exposes a `setState` method that you can use to modify your `state` property and automatically `trigger` the change: | ||
```javascript | ||
var Actions = Reflux.createActions(["increment"]); | ||
class CounterStore extends Reflux.Store | ||
{ | ||
constructor() { | ||
super(); | ||
this.listenables = Actions; | ||
this.state = {count:0}; | ||
} | ||
onIncrement() { | ||
var cnt = this.state.count; | ||
this.setState({count:cnt+1}); | ||
} | ||
} | ||
``` | ||
`this.listenables` also accepts an array of actions in the event you want your store to listen to actions from other places as well: | ||
```javascript | ||
var Actions1 = Reflux.createActions(["increment"]); | ||
var Actions2 = Reflux.createActions(["decrement"]); | ||
class CounterStore extends Reflux.Store | ||
{ | ||
constructor() { | ||
super(); | ||
this.listenables = [Actions1, Actions2]; | ||
this.state = {count:0}; | ||
onFirstAction() | ||
{ | ||
// calls on Actions.firstAction(); | ||
} | ||
onIncrement() { | ||
var cnt = this.state.count; | ||
this.setState({count:cnt+1}); | ||
} | ||
onDecrement() { | ||
var cnt = this.state.count; | ||
this.setState({count:cnt-1}); | ||
} | ||
onSecondAction() | ||
{ | ||
// calls on Actions.secondAction(); | ||
} | ||
} | ||
``` | ||
One thing you may notice is that the original style `Reflux.createStore` creates an actual instance (as opposed to a class) which is what is assigned to `this.store` in the `Reflux.Component`. Extending `Reflux.Store` means you just have a class, not an instance of anything. Of course you can instantiate and use that store; however, if you just assign the class itself to `this.store` or `this.stores` in the `Reflux.Component` then it will automatically create a singleton instance of the store class (or use a previously created singleton instance of it if another component has already done so in its own construction). So, for example, to utilize the `Reflux.Store` store in the last example within a `Reflux.Component` class would look like this: | ||
#### More on Stores: | ||
Reflux stores are very powerful. They can even do things like contribute to a global state that can be read and set for partial or full-state time-travel, debugging, etc. | ||
```javascript | ||
class Counter extends Reflux.Component | ||
{ | ||
constructor(props) { | ||
super(props); | ||
this.state = {}; | ||
this.store = CounterStore; // <- just assign the class itself | ||
} | ||
render() { | ||
return <p>Count: {this.state.count}</p>; | ||
} | ||
} | ||
``` | ||
See [Reflux Store Documentation](docs/stores/) to learn more about stores. | ||
**Note!** `Reflux.Store` still works with instances of stores (i.e. the class must get intantiated). Assigning the class itself to `this.store` just allows Reflux to handle the instantiation and do some internal things that allow features like global state tracking. it does *not* mean that the class itself is the store. Internally Reflux creates and utilizes a singleton instance of the class. After mounting you may access that singleton instance of the class via `MyStoreClass.singleton`. | ||
-------------------------- | ||
#### Using Reflux.Store without a component | ||
## Hooking Stores to Components | ||
With to ability to do so much via global states (covered in the next section), and the fact that that functionality is tied to `Reflux.Store`, being able to properly utilize `Reflux.Store` on its own (without binding to a React component) becomes useful. However, just using `new MyStoreClass()` isn't enough, as it has to tie itself into the Reflux global state as a singleton. Therefore Reflux exposes an API for getting a properly globalized singleton instance of a `Reflux.Store` extended class without having to tie it to a React component. You do this via the following: | ||
Once you've created actions and stores, now the last step in working RefluxJS is to hook those stores to a React component. | ||
```javascript | ||
var mySingleton = Reflux.initializeGlobalStore(MyClassName); | ||
``` | ||
This is done as simply as extending `Reflux.Component` instead of `React.Component` and setting the store(s) to use. `Reflux.Component` itself extends `React.Component`, so you use them the exact same. The only difference is that `Reflux.Component` allows you to set stores for the component to get state from: | ||
When done this way the singleton instance of your `Reflux.Store` class can, externally, be used much like a non-ES6 store created via `Reflux.createStore` except with the advantages that it: 1) is written in the `Reflux.Store` ES6 syntax and 2) it ties in with the global state being tracked by Reflux. | ||
Note: your store _must_ be set up with an `id` to be used this way. | ||
Note: even after instantiating with `Reflux.initializeGlobalStore` you can still later assign the class name itself to `this.store` or `this.stores` in a `Reflux.Component`. The component will recognize that a singleton for the class has already been created and use that singleton. | ||
##### A deeper understanding: | ||
To avoid confusion I want to better explain what `Reflux.initializeGlobalStore` is for on a deeper level. This is also a good section to read for people that just want a better understanding of working with `Reflux.Store` in general. | ||
When you define a store in ES6 `Reflux.Store` syntax you are creating a class, not an instance of a class. For Reflux to use that store internally an instance of the class must be created. But it's also important that only **one** instance of that store class get created (i.e. a singleton) so that each component using it is using the same store instance. That is why you're told to assign the class itself to `this.store` or `this.stores` in a `Reflux.Component`. Assigning an instance works...but if you assign the class itself then it can use its own internal logic to say *"Is a singleton already made for this class? If yes, use that singleton. If not, then make it first, then use it."* therefore automatically making sure only one singleton instance is used everywhere. On top of that (if the class has an id) it also makes sure that the global state is aware of that store so that it can track it. | ||
This works great as long as you're going to be using that store in a component. But what if you don't want to use it in any components? | ||
If you don't care about the global state knowing about it and tracking it then it's easy. Since no components are using it then there's no chance of multiple store instances accidentally being created, so just use `var store = new MyStoreClass();` like any other class you'd be instantiating. If you wanted to be thorough and make your instance in the same singleton fashion as is done internally (just in case some later component decides to use that class without your knowledge) then you could go `var store = new MyStoreClass(); MyStoreClass.singleton = store;` and then any future component usage would know about, and use, that singleton instance. | ||
But that still leaves a scenario out in the cold: needing to intantiate a store without it being attached to any components to handle it for you, but *also* wanting that store to be properly tracked by the global state. That is where `Reflux.initializeGlobalStore` can be used to create your singleton instance and handle what needs to be handled internally to track the global state. | ||
Another possible scenario would be if you need to access the singleton instance of the store *before* the `componentWillMount` part of any component's lifecycle (which is where the component would set up the singleton). You can use `Reflux.initializeGlobalStore` to create a singleton that you can access sooner. You can *still* assign the class itself to `this.store` or `this.stores` in any components though! The component will know that a singleton has already been created and automatically use it, there is no need for you to manually track the singleton for the components just because you used `Reflux.initializeGlobalStore` in order to get access to that singleton sooner! | ||
#### Mapping Stores to Components with `mapStoreToState` | ||
Reflux's ES6 stores and components work together fairly well in a very declarative syntax by simply assigning stores to components via the component's `this.store` and `this.stores` properties, and that functionality rounds itself out by adding some filtering ability with `this.storeKeys`. With enough thought given to architecture this can get you almost everything you need in a declarative syntax that is easy for you to write and others to read. Therefore it's highly suggested that you put the time into planning your architecture to be able to use those for connecting stores and components. | ||
However, there still exists a need to have deeper control and to be able to map stores to component states with your own custom logic in some cases. For that each `Reflux.Component` will have a `this.mapStoreToState` method. This is **completely separate** from the previously mentioned declarative side of things (such as `this.store`). That means you should not have the same store attached to a component both via `this.store` *and* using `this.mapStoreToState`, and also that this method is completely unaffected by `this.storeKeys`. These differing methods can both be used within a single component, they just shouldn't be both used for the *same store* within the same component. | ||
This method takes 2 arguments: the `Reflux.Store` you want mapped to the component state (either the class itself or the singleton instance) and a mapping function supplied by you. The mapping function will be called any time the store instance's `setState` is used to change the state of the store. The mapping function takes an argument which will be the state change object from the store for that particular change. It needs to return an object which will then be mapped to the component state (similar to if that very returned object were used in the component's `setState`). If an object with no properties is returned then the component will *not* re-render. The mapping function is also called with its `this` keyword representing the component, so comparing store values to current component state values via `this.state` is possible as well. | ||
```javascript | ||
class Counter extends Reflux.Component | ||
class MyComponent extends Reflux.Component | ||
{ | ||
constructor(props) { | ||
constructor(props) | ||
{ | ||
super(props); | ||
this.mapStoreToState(MyStoreClass, function(fromStore){ | ||
var obj = {}; | ||
if (fromStore.color) | ||
obj.color = fromStore.color; | ||
if (fromStore.data && fromStore.data.classToUse) | ||
obj.class = fromStore.data.classToUse; | ||
return obj; | ||
}); | ||
this.state = {}; // our store will add its own state to the component's | ||
this.store = StatusStore; // <- just assign the store class itself | ||
} | ||
render() { | ||
return <p className={this.state.class}>The color is: {this.state.color}</p>; | ||
render() | ||
{ | ||
var flag = this.state.flag; // <- flag is mixed in from the StatusStore | ||
return <div>User is {flag}</div> | ||
} | ||
@@ -916,110 +197,46 @@ } | ||
In the above example `MyStoreClass` could have lots of state properties, but we use a bit of logic to 1) only trigger a re-render if the store's `state.color` or `state.data.classToUse` were among the parts of the state involved in the store's `setState` call (because, remember, if the returned object has no properties no re-render happens), and 2) to map the stores `state.color` straight to the component's `state.color`, but the store's `state.data.classToUse` to the component's `state.class`. | ||
When the component mounts it will either create a singleton instance of `StatusStore` (if one isn't already made) or use an already made singleton (if it was already created by another component that uses the store). | ||
Note that the example function above is merely that: an example. Whatever sort of logic you want to apply to get from the change object given by the store to how you want that to change the state of your component is fair game, except that you should not mutate the incoming data itself. | ||
Of important note is that you can: | ||
1. Set multiple stores by setting `this.stores` (the plural) and setting it to an Array of store classes. | ||
2. Set a `this.storeKeys` Array to restrict only certain parts of the store being mixed into the component state. | ||
### Utilizing Reflux.GlobalState | ||
There is also a `mapStoreToState` method in the documentation for those that want absolute control over how a store's state is mapped to a component. | ||
Another neat feature that the ES6 implementation of Reflux has is the ability to track a global state of all stores in use, as well as initialize all stores in use to a predefined global state. It happens internally too, so you don't have to do hardly anything to make it happen. This would be useful for many things, including tracking the state of an application and going back to that same state the next time the app is used. | ||
To make it happen you just have to use ES6 style reflux classes and stores like explained in the last couple sections and define a static `id` property in your `Reflux.Store` definition. That id will then be used as a property name within the `Reflux.GlobalState` object for the property holding that store's current state. Then you just need to make sure to use `setState` to modify the state of the `Reflux.Store` instead of mutating the state directly. After that the `Reflux.GlobalState` object will reflect a collection of all your stores at all times once the components using those stores are mounted. An example using the example above: | ||
```javascript | ||
class CounterStore extends Reflux.Store | ||
class MyComponent extends Reflux.Component | ||
{ | ||
constructor() { | ||
super(); | ||
this.listenables = Actions; | ||
this.state = {count:0}; | ||
constructor(props) | ||
{ | ||
super(props); | ||
this.state = {type:'admin'}; // <- note that we can still use normal state | ||
this.stores = [StatusStore, AnotherStore]; | ||
this.storeKeys = ['flag', 'info']; | ||
} | ||
onIncrement() { | ||
var cnt = this.state.count; | ||
this.setState({count:cnt+1}); | ||
} | ||
static get id() { | ||
return 'counterstore'; | ||
} | ||
} | ||
// ... make component and render as normal ... | ||
console.log(Reflux.GlobalState); // <- would be: {'counterstore':{'count':0}} | ||
``` | ||
Notice that you can only read the GlobalState **after** the components using the stores have been mounted. Up until then is the time where you can manually set the `Reflux.GlobalState` in order to initialize the entire app in a state of your choosing (or a previous state you recorded earlier). For example we could do this: | ||
```javascript | ||
class CounterStore extends Reflux.Store | ||
{ | ||
constructor() { | ||
super(); | ||
this.listenables = Actions; | ||
this.state = {count:0}; | ||
render() | ||
{ | ||
var flag = this.state.flag; | ||
var info = this.state.info; | ||
var type = this.state.type; | ||
return <div>User is {flag}, info: {info}, type: {type}</div> | ||
} | ||
onIncrement() { | ||
var cnt = this.state.count; | ||
this.setState({count:cnt+1}); | ||
} | ||
static get id() { | ||
return 'counterstore'; | ||
} | ||
} | ||
Reflux.GlobalState = {'counterstore':{'count':50}}; | ||
// ... make component and render as normal ... | ||
// at this point it would render with a count of 50! | ||
``` | ||
One of the most useful ways you could do this is to store a `Reflux.GlobalState` state as a JSON string in order to implement it again the next time the app starts up and have the user begin right where they left off. | ||
The above will mix in properties from the state of both `StatusStore` and `AnotherStore`. However, because of `this.storeKeys` it will only mix in the properties `flag` and `info` from them. So any other properties within those stores will not get mixed in. So even if a store contained a `type` property in its state it would not get mixed in, and the `type` value we set as a normal part of the component state is safe. | ||
#### Reflux.setGlobalState and Reflux.getGlobalState | ||
#### More on using Reflux style components: | ||
Directly accessing `Reflux.GlobalState` is a fine way to do set the starting state of an app and to do automated testing, but it is also helpful to be able to manipulate the global state while the app is running as well. To do this Reflux exposes a `Reflux.getGlobalState()` function and a `Reflux.setGlobalState()` function. The former allows you to get a deep copy of the current global state (so that the copy will not mutate as the global state itself continues to mutate) and the latter allows you to set part or all of the global state at any time in the program. Between these two functions things like state time-travel, undo/redo, and move-by-move tracking become relatively easy. | ||
Reflux's simple and intuitive way of integrating stores into components is easy and powerful. You can aggregate stores together on a component-by-component basis, filter which parts of the stores come through and which don't, or even do a detailed manual mapping of exactly how you want the state from stores to map to the state in a particular component. | ||
### Making sure Reflux.Component is available | ||
See [Reflux Style Component Documentation](docs/components/) to learn more. | ||
`Reflux.Component` extends `React.Component`. Therefore Reflux needs to be able to access React in order to expose it. In the browser as long as React is loaded before Reflux then Reflux will automatically find it. Likewise in node-like environments where `require('react')` will function Reflux will try to access React that way. So in almost all situations Reflux will find React on its own. | ||
----------------------------------------- | ||
However, Reflux also exposes the method `Reflux.defineReact` that you can use to manually give Reflux a reference to the React object in case you need to: | ||
## Documentation | ||
```javascript | ||
// only needed if, for some reason, Reflux can't get reference to React: | ||
var React = /* however you access React */; | ||
Reflux.defineReact(React); | ||
// now Reflux.Component is accessible! | ||
``` | ||
What you've just read is a "view from 10,000 feet" type overview of getting started with RefluxJS. For serious learning see the [documentation](docs/). | ||
### Extending a 3rd Party Class | ||
Sometimes 3rd party libraries will have their own class that extends `React.Component` that they require you to use. Reflux handles this by exposing the `Reflux.Component.extend` method. If you have such a 3rd party class you can pass that class to this method and it will return a version of `Reflux.Component` that extends it instead of extending `React.Component` directly. Example: | ||
```javascript | ||
import {ThirdPartyComponent} from 'third-party'; | ||
var RefluxThirdPartyComponent = Reflux.Component.extend(ThirdPartyComponent); | ||
class MyComponent extends RefluxThirdPartyComponent | ||
{ | ||
// ... | ||
} | ||
``` | ||
[Back to top](#content) | ||
## Colophon | ||
[List of contributors](https://github.com/spoike/reflux/graphs/contributors) is available on Github. | ||
This project is licensed under [BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause). Copyright (c) 2014, Mikael Brassman. | ||
For more information about the license for this particular project [read the LICENSE.md file](LICENSE.md). | ||
This project uses [eventemitter3](https://github.com/3rd-Eden/EventEmitter3), which [is currently MIT licensed](https://github.com/3rd-Eden/EventEmitter3/blob/master/LICENSE). | ||
[npm-image]: http://img.shields.io/npm/v/reflux.svg | ||
@@ -1026,0 +243,0 @@ [downloads-image]: http://img.shields.io/npm/dm/reflux.svg |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
225575
2426
249
+ Addedreflux-core@1.0.0(transitive)
- Removedreflux-core@0.4.3(transitive)
Updatedreflux-core@^1.0.0