Comparing version 0.11.0 to 0.12.0
@@ -25,6 +25,5 @@ "use strict"; | ||
var LAST_SNAPSHOT = Symbol("last snapshot storage"); | ||
var LIFECYCLE = Symbol("store lifecycle listeners"); | ||
var LISTENERS = Symbol("stores action listeners storage"); | ||
var STATE_CONTAINER = VariableSymbol("the state container"); | ||
var STORE_BOOTSTRAP = Symbol("event handler onBootstrap"); | ||
var STORE_SNAPSHOT = Symbol("event handler onTakeSnapshot"); | ||
@@ -59,11 +58,8 @@ var formatAsConstant = function (name) { | ||
this[EE] = new EventEmitter(); | ||
this[LIFECYCLE] = {}; | ||
this[STATE_CONTAINER] = state; | ||
this[EE] = new EventEmitter(); | ||
if (state.onBootstrap) { | ||
this[STORE_BOOTSTRAP] = state.onBootstrap.bind(state); | ||
} | ||
if (state.onTakeSnapshot) { | ||
this[STORE_SNAPSHOT] = state.onTakeSnapshot.bind(state); | ||
} | ||
assign(this[LIFECYCLE], state[LIFECYCLE]); | ||
// Register dispatcher | ||
@@ -76,2 +72,6 @@ this.dispatchToken = dispatcher.register(function (payload) { | ||
}); | ||
if (this[LIFECYCLE].init) { | ||
this[LIFECYCLE].init(); | ||
} | ||
} | ||
@@ -115,6 +115,6 @@ | ||
var ActionCreator = (function () { | ||
function ActionCreator(dispatcher, name, action, actions) { | ||
function ActionCreator(alt, name, action, actions) { | ||
_classCallCheck(this, ActionCreator); | ||
this[ACTION_DISPATCHER] = dispatcher; | ||
this[ACTION_DISPATCHER] = alt.dispatcher; | ||
this[ACTION_UID] = name; | ||
@@ -124,2 +124,3 @@ this[ACTION_HANDLER] = action.bind(this); | ||
this.actions = actions; | ||
this.alt = alt; | ||
} | ||
@@ -144,2 +145,6 @@ | ||
var StoreMixin = { | ||
on: function on(lifecycleEvent, handler) { | ||
this[LIFECYCLE][lifecycleEvent] = handler.bind(this); | ||
}, | ||
bindAction: function bindAction(symbol, handler) { | ||
@@ -201,9 +206,7 @@ if (!symbol) { | ||
var setAppState = function (instance, data) { | ||
var setAppState = function (instance, data, onStore) { | ||
var obj = JSON.parse(data); | ||
Object.keys(obj).forEach(function (key) { | ||
assign(instance.stores[key][STATE_CONTAINER], obj[key]); | ||
if (instance.stores[key][STORE_BOOTSTRAP]) { | ||
instance.stores[key][STORE_BOOTSTRAP](); | ||
} | ||
onStore(instance.stores[key]); | ||
}); | ||
@@ -214,4 +217,4 @@ }; | ||
return JSON.stringify(Object.keys(instance.stores).reduce(function (obj, key) { | ||
if (instance.stores[key][STORE_SNAPSHOT]) { | ||
instance.stores[key][STORE_SNAPSHOT](); | ||
if (instance.stores[key][LIFECYCLE].snapshot) { | ||
instance.stores[key][LIFECYCLE].snapshot(); | ||
} | ||
@@ -247,2 +250,3 @@ obj[key] = instance.stores[key].getState(); | ||
this.dispatcher = new Dispatcher(); | ||
this.actions = {}; | ||
this.stores = {}; | ||
@@ -262,8 +266,10 @@ this[LAST_SNAPSHOT] = null; | ||
function Store() { | ||
this[LIFECYCLE] = {}; | ||
this[LISTENERS] = {}; | ||
StoreModel.call(this); | ||
} | ||
Store.prototype = StoreModel.prototype; | ||
Store.prototype[LISTENERS] = {}; | ||
assign(Store.prototype, StoreMixin, { | ||
_storeName: key, | ||
alt: this, | ||
dispatcher: this.dispatcher, | ||
@@ -294,3 +300,2 @@ getInstance: function () { | ||
var exportObj = arguments[1] === undefined ? {} : arguments[1]; | ||
var key = ActionsClass.displayName || ActionsClass.name; | ||
var actions = assign({}, getInternalMethods(ActionsClass.prototype, builtInProto)); | ||
@@ -323,6 +328,6 @@ | ||
var constant = formatAsConstant(action); | ||
var actionName = Symbol("action " + key + ".prototype." + action); | ||
var actionName = Symbol["for"]("action " + action); | ||
// Wrap the action so we can provide a dispatch method | ||
var newAction = new ActionCreator(_this.dispatcher, actionName, actions[action], obj); | ||
var newAction = new ActionCreator(_this, actionName, actions[action], obj); | ||
@@ -356,3 +361,7 @@ // Set all the properties on action | ||
value: function rollback() { | ||
setAppState(this, this[LAST_SNAPSHOT]); | ||
setAppState(this, this[LAST_SNAPSHOT], function (store) { | ||
if (store[LIFECYCLE].rollback) { | ||
store[LIFECYCLE].rollback(); | ||
} | ||
}); | ||
}, | ||
@@ -370,3 +379,7 @@ writable: true, | ||
setAppState(this, snapshot); | ||
setAppState(this, snapshot, function (store) { | ||
if (store[LIFECYCLE].init) { | ||
store[LIFECYCLE].init(); | ||
} | ||
}); | ||
}, | ||
@@ -387,3 +400,8 @@ writable: true, | ||
value: function bootstrap(data) { | ||
setAppState(this, data); | ||
setAppState(this, data, function (store) { | ||
if (store[LIFECYCLE].bootstrap) { | ||
store[LIFECYCLE].bootstrap(); | ||
} | ||
}); | ||
if (typeof window !== "undefined") { | ||
@@ -398,2 +416,33 @@ if (this[BOOTSTRAP_FLAG]) { | ||
configurable: true | ||
}, | ||
addActions: { | ||
// Instance type methods for injecting alt into your application as context | ||
value: function addActions(name, ActionsClass) { | ||
this.actions[name] = this.createActions(ActionsClass); | ||
}, | ||
writable: true, | ||
configurable: true | ||
}, | ||
addStore: { | ||
value: function addStore(name, StoreModel) { | ||
this.createStore(StoreModel, name); | ||
}, | ||
writable: true, | ||
configurable: true | ||
}, | ||
getActions: { | ||
value: function getActions(name) { | ||
return this.actions[name]; | ||
}, | ||
writable: true, | ||
configurable: true | ||
}, | ||
getStore: { | ||
value: function getStore(name) { | ||
return this.stores[name]; | ||
}, | ||
writable: true, | ||
configurable: true | ||
} | ||
@@ -400,0 +449,0 @@ }); |
{ | ||
"name": "alt", | ||
"version": "0.11.0", | ||
"version": "0.12.0", | ||
"description": "A flux implementation", | ||
@@ -15,3 +15,4 @@ "main": "dist/alt.js", | ||
"coveralls": "^2.11.2", | ||
"istanbul": "^0.3.5" | ||
"istanbul": "^0.3.5", | ||
"mocha": "^2.1.0" | ||
}, | ||
@@ -23,6 +24,10 @@ "repository": { | ||
"scripts": { | ||
"build": "6to5 ./src/alt.js > dist/alt.js", | ||
"prepublish": "npm run build", | ||
"coverage": "npm test; 6to5 --runtime test.js > coverage-test.js; istanbul cover coverage-test.js", | ||
"test": "6to5 --runtime src/alt.js > src/coverage-alt.js; 6to5-node test.js;" | ||
"build": "npm run build-alt; npm run build-alt-runtime", | ||
"build-alt": "6to5 ./src/alt.js > dist/alt.js", | ||
"build-alt-runtime": "6to5 --runtime src/alt.js > dist/alt-with-runtime.js", | ||
"build-test": "6to5 --runtime test/index.js > test/compiled.js", | ||
"coverage": "istanbul cover _mocha -- -u exports -R list test/compiled.js", | ||
"prepublish": "npm test", | ||
"test": "npm run build; npm run build-test; npm run tests", | ||
"tests": "mocha -u exports -R list test/compiled.js" | ||
}, | ||
@@ -29,0 +34,0 @@ "keywords": [ |
149
README.md
@@ -16,3 +16,3 @@ # alt | ||
Alt is a [flux](http://facebook.github.io/flux/docs/overview.html) implementation that is [small](https://github.com/goatslacker/alt/blob/master/src/alt.js) (~3.9kb & 300 LOC), [well tested](https://github.com/goatslacker/alt/blob/master/test.js), [terse](https://github.com/goatslacker/alt#differences), and meant to be used with ES6. | ||
Alt is a [flux](http://facebook.github.io/flux/docs/overview.html) implementation that is [small](https://github.com/goatslacker/alt/blob/master/src/alt.js) (~4.3kb & 400 LOC), [well tested](https://github.com/goatslacker/alt/blob/master/test.js), [terse](https://github.com/goatslacker/alt#differences), [insanely flexible](#flexibility), and [forward thinking](#es6). | ||
@@ -109,13 +109,15 @@ Some boilerplate has been removed from flux such as the [JS "constants"](https://github.com/facebook/flux/blob/master/examples/flux-chat/js/constants/ChatConstants.js), | ||
### ES Versions Disclaimer | ||
### ES6 | ||
Alt depends on ES5 features, the good news is so does React. You can use [es5-shim](https://github.com/es-shims/es5-shim) | ||
to support those pesky old browsers. | ||
Alt is written in, and encourages ES6. It is completely optional but it is pleasant to write. | ||
Alt encourages ES6 features, the good news is it's pleasant to write. You can use the es6 transpiler that comes | ||
with react courtesy of [jstransform](https://github.com/facebook/jstransform) or you can use your own favorite ES6 transpiler: | ||
[6to5](https://6to5.org/), [es-next](https://esnext.github.io/esnext/), or [any es6 transpiler](https://www.npmjs.com/search?q=es6) you fancy. | ||
You can use the es6 transpiler that comes with react courtesy of | ||
[jstransform](https://github.com/facebook/jstransform) or you can use one of the other popular ES6 transpilers: | ||
[6to5](https://6to5.org/) or [traceur](https://github.com/google/traceur-compiler). | ||
You won't need an [es6-shim](https://github.com/paulmillr/es6-shim) but you can use one for further goodies in your javascripts. | ||
Alt does depend on ES5 features, the good news is so does React. You can use [es5-shim](https://github.com/es-shims/es5-shim) | ||
to support those pesky old browsers. | ||
### Creating Actions | ||
@@ -268,3 +270,3 @@ | ||
#### Disclaimer | ||
#### Important Note | ||
@@ -527,10 +529,66 @@ All defined methods in your Store class **will not** be available on the store instance. They are accessible within the class but not on the returned | ||
### Flushing | ||
`flush :: String` | ||
Flush takes a snapshot of the current state and then resets all the stores back to their original initial state. This is useful if you're using alt stores as singletons and doing server side rendering because of concurrency. In this particular scenario you would load the data in via `bootstrap` and then use `flush` to take a snapshot, render the data, and reset your stores so they are ready for the next request. | ||
### Recycling | ||
`recycle :: ?...String -> undefined` | ||
If you wish to reset a particular, or all, store's state back to their original initial state you would call `recycle`. Recycle takes an optional number of arguments as strings which correspond to the store's names you would like reset. If no argument is provided then all stores are reset. | ||
### Life Cycle Methods | ||
When bootstrapping or snapshotting there are special methods you can assign to your store to ensure any bookeeping that needs to be done. | ||
When bootstrapping, snapshotting, or recycling there are special methods you can assign to your store to ensure any bookeeping that needs to be done. You would place these in your store's constructor. | ||
`onBootstrap()` is a method which is called after the store has been bootstrapped. Here you can add some logic to take your bootstrapped data and manipulate it. | ||
`bootstrap` is called after the store has been bootstrapped. Here you can add some logic to take your bootstrapped data and manipulate it. | ||
`onTakeSnapshot()` is a method which is called before the store's state is serialized. Here you can perform any final tasks you need to before the state is saved. | ||
```js | ||
class Store { | ||
constructor() { | ||
this.on('bootstrap', () => { | ||
// do something here | ||
}) | ||
} | ||
} | ||
``` | ||
`snapshot` is called before the store's state is serialized. Here you can perform any final tasks you need to before the state is saved. | ||
```js | ||
class Store { | ||
constructor() { | ||
this.on('snapshot', () => { | ||
// do something here | ||
}) | ||
} | ||
} | ||
``` | ||
`init` is called when the store is initialized as well as whenever a store is recycled. | ||
```js | ||
class Store { | ||
constructor() { | ||
this.on('init', () => { | ||
// do something here | ||
}) | ||
} | ||
} | ||
``` | ||
`rollback` is called whenever all the stores are rolled back. | ||
```js | ||
class Store { | ||
constructor() { | ||
this.on('rollback', () => { | ||
// do something here | ||
}) | ||
} | ||
} | ||
``` | ||
### Single Dispatcher | ||
@@ -555,2 +613,57 @@ | ||
### Flexibility | ||
You can choose to use alt in many ways just like you'd use flux. This means your asynchronous data fetching can live in the actions, or they can live in the stores. | ||
Stores may also be traditional singletons as in flux, or you can create an instance and have multilple store copies. This leads us into server side rendering. | ||
### Server Side Rendering | ||
Alt was built with isomorphism in mind. This means that you can run full flux server-side and pick back up on the client-side. | ||
There are two options for using flux on the server: | ||
* Keep stores as singletons, keep data loading synchronous, bootstrap, and flush. | ||
* Create multiple instances of flux and inject the context into your app. | ||
#### Stores as Singletons | ||
With this approach your stores are singletons. | ||
Any actions that load data must be synchronous, meaning you can fetch your data outside of actions and stores, and once done you fire off a synchronous action which loads | ||
the store. Alternatively, you can gather all of your data, and once complete, you call `bootstrap()` which seeds all the stores with some initial data. | ||
Once you've completed loading the stores with data you call `flush()` which takes a snapshot to send to the client and then resets all the stores' state back to their initial state. This allows the stores to be ready for the next server request. | ||
#### Flux Instances | ||
If you're afraid of singletons, or if you want to skip synchronous actions or data loading you may want to create separate instances of flux for every server request. Taking this approach means you're making the trade-off of injecting the flux instance into your application in order to retrieve the stores and use the actions. This approach is similar to how [fluxible](https://github.com/yahoo/fluxible) solves isomorphic applications. Creating a new alt instances is fairly simple. | ||
```js | ||
class Flux extends Alt { | ||
constructor() { | ||
super() | ||
this.addActions('myActions', ActionCreators) | ||
this.addStore('storeName', Store) | ||
} | ||
} | ||
var flux = new Flux() | ||
// sample using react... | ||
React.render( | ||
<App flux={flux} />, | ||
document.body | ||
) | ||
// retrieving stores | ||
flux.getStore('storeName').getState() | ||
// actions | ||
flux.getActions('myActions') | ||
``` | ||
#### Picking back up on the client | ||
To help facilitate with isomorphism alt recommends you use [iso](https://github.com/goatslacker/iso), a helper function which serializes the data on the server into markup and then parses that data back into usable JavaScript on the client. Iso is a great complement to alt for a full-stack flux approach. | ||
## Examples | ||
@@ -610,13 +723,5 @@ | ||
## Other Flux Implementations | ||
[Reflux](https://github.com/spoike/refluxjs) is a fun to work with and terse "flux implementation". | ||
It's not actually flux, but it's still a pleasure to use. Alt brings the terseness of reflux to actual flux. | ||
[Fluxxor](http://fluxxor.com/) is a neat implementation of flux with good documentation and examples. | ||
[...others](https://www.npmjs.com/search?q=flux) there are actually plenty of flux implementations. | ||
## TL;DR | ||
* Isomorphic | ||
* Pure Flux | ||
@@ -629,9 +734,9 @@ * No constants | ||
* Bootstrap components on app load | ||
* Isomorphic | ||
* Light-weight and terse | ||
* ES6 Syntax, code your actions and stores with classes | ||
* Flexible | ||
* Immutable stores | ||
* Single dispatcher | ||
* Global listening for debugging | ||
* Small library ~3.9kb gzipped and less than 300 LOC | ||
* Small library | ||
@@ -638,0 +743,0 @@ ## License |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
105418
11
2022
739
4
1