Skip to content
This repository has been archived by the owner on Jul 15, 2019. It is now read-only.

Commit

Permalink
[resolves #16] Added utils for creating stores
Browse files Browse the repository at this point in the history
  • Loading branch information
mridgway committed Aug 15, 2014
1 parent f3f2448 commit 44ff5ef
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 56 deletions.
31 changes: 21 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ Takes an object representing the state of the Dispatchr instance (usually retrie

## Store Interface

Dispatchr expects that your stores use the following interface:
We have provided utilities for creating stores in a couple of forms:

* `require('dispatchr/utils/createStore')` returns a `React.createClass`-like function for creating stores.
* `require('dispatchr/utils/BaseStore')` returns an extendable class.

You are not required to use these if you want to keep your stores completely decoupled from the dispatcher. Dispatchr expects that your stores use the following interface:

### Constructor

Expand All @@ -70,10 +75,14 @@ The store should have a constructor function that will be used to instantiate yo
* `dispatcherInterface.getStore(store)`
* `dispatcherInterface.waitFor(store[], callback)`

The constructor is also where the initial state of the store should be initialized.

```js
function ExampleStore(dispatcher) {
this.dispatcher = dispatcher;
this.getInitialState();
if (this.initialize) {
this.initialize();
}
}
```

Expand Down Expand Up @@ -113,25 +122,27 @@ ExampleStore.prototype.handleNavigate = function (payload) {
};
```

### getState()
### dehydrate()

The store should implement this function that will return a serializable data object that can be transferred from the server to the client and also be used by your components.
The store should define this function to dehydrate the store if it will be shared between server and client. It should return a serializable data object that will be passed to the client.

```js
ExampleStore.prototype.getState = function () {
ExampleStore.prototype.dehydrate = function () {
return {
navigating: this.navigating
};
};
```

### dehydrate()

The store can optionally define this function to customize the dehydration of the store. It should return a serializable data object that will be passed to the client.

### rehydrate(state)

The store can optionally define this function to customize the rehydration of the store. It should restore the store to the original state using the passed `state`.
The store should define this function to rehydrate the store if it will be shared between server and client. It should restore the store to the original state using the passed `state`.

```js
ExampleStore.prototype.rehydrate = function (state) {
this.navigating = state.navigating;
};
```

## License

Expand Down
3 changes: 1 addition & 2 deletions lib/Dispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ module.exports = function () {
debug(actionName + ' does not have any registered handlers');
return;
}
debug('dispatching ' + actionName, payload);
this.currentAction = new Action(actionName, payload);
var self = this,
handlerFns = {};
Expand Down Expand Up @@ -169,8 +170,6 @@ module.exports = function () {
var store = self.storeInstances[storeName];
if (store.dehydrate) {
stores[storeName] = store.dehydrate();
} else {
stores[storeName] = store.getState();
}
});
return {
Expand Down
65 changes: 26 additions & 39 deletions tests/mock/DelayedStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,29 @@
* Copyright 2014, Yahoo! Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
var util = require('util'),
EventEmitter = require('events').EventEmitter;

function DelayedStore(dispatcher) {
this.dispatcher = dispatcher;
this.getInitialState();
}

DelayedStore.storeName = 'DelayedStore';
util.inherits(DelayedStore, EventEmitter);

DelayedStore.prototype.getInitialState = function () {
this.state = {};
};

DelayedStore.prototype.delay = function (payload) {
var self = this;
self.called = true;
self.state.page = 'delay';
self.state.final = true;
};

DelayedStore.prototype.getState = function () {
return this.state;
};

DelayedStore.prototype.dehydrate = function () {
return this.state;
};

DelayedStore.prototype.rehydrate = function (state) {
this.state = state;
};

DelayedStore.handlers = {
'DELAY': 'delay'
};

module.exports = DelayedStore;
var createStore = require('../../utils/createStore');

module.exports = createStore({
storeName: 'DelayedStore',
handlers: {
'DELAY': 'delay'
},
initialize: function () {
this.state = {};
},
delay: function (payload) {
var self = this;
self.called = true;
self.state.page = 'delay';
self.state.final = true;
},
getState: function () {
return this.state;
},
dehydrate: function () {
return this.state;
},
rehydrate: function (state) {
this.state = state;
}
});
13 changes: 8 additions & 5 deletions tests/mock/Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
var util = require('util'),
EventEmitter = require('events').EventEmitter,
BaseStore = require('../../utils/BaseStore'),
DelayedStore = require('./DelayedStore');

function Store(dispatcher) {
this.dispatcher = dispatcher;
this.getInitialState();
BaseStore.call(this, dispatcher);
}

Store.storeName = 'Store';
util.inherits(Store, EventEmitter);
util.inherits(Store, BaseStore);

Store.prototype.getInitialState = function () {
Store.prototype.initialize = function () {
this.state = {
called: false
};
Expand All @@ -41,6 +40,10 @@ Store.prototype.getState = function () {
return this.state;
};

Store.prototype.dehydrate = function () {
return this.state;
};

Store.prototype.rehydrate = function (state) {
this.state = state;
};
Expand Down
48 changes: 48 additions & 0 deletions utils/BaseStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright 2014, Yahoo! Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
'use strict';

var util = require('util'),
EventEmitter = require('events').EventEmitter,
CHANGE_EVENT = 'change';

/**
* @class BaseStore
* @param dispatcher The dispatcher interface
* @constructor
*/
function BaseStore(dispatcher) {
this.dispatcher = dispatcher;
if (this.initialize) {
this.initialize();
}
}

util.inherits(BaseStore, EventEmitter);

/**
* Add a listener for the change event
* @param {Function} callback
*/
BaseStore.prototype.addChangeListener = function(callback) {
this.on(CHANGE_EVENT, callback);
};

/**
* Remove a listener for the change event
* @param {Function} callback
*/
BaseStore.prototype.removeChangeListener = function(callback) {
this.removeListener(CHANGE_EVENT, callback);
};

/**
* Emit a change event
*/
BaseStore.prototype.emitChange = function() {
this.emit(CHANGE_EVENT);
};

module.exports = BaseStore;
47 changes: 47 additions & 0 deletions utils/createStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright 2014, Yahoo! Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
'use strict';

var util = require('util'),
BaseStore = require('./BaseStore'),
IGNORE_ON_PROTOTYPE = ['statics', 'storeName', 'handlers'];

/**
* Helper for creating a store class
* @param {Object} spec
* @param {String} spec.storeName The name of the store
* @param {Object} spec.handlers Hash of action name to method name of action handlers
* @param {Function} spec.initialize Function called during construction for setting the default state
* @param {Function} spec.dehydrate Function that returns serializable data to send to the client
* @param {Function} spec.rehydrate Function that takes in serializable data to rehydrate the store
*/
module.exports = function createStore(spec) {
spec.statics = spec.statics || {};
if (!spec.storeName && !spec.statics.storeName) {
throw new Error('createStore called without a storeName');
}
var Store = function (dispatcher) {
BaseStore.call(this, dispatcher);
};

util.inherits(Store, BaseStore);

if (spec.statics) {
Object.keys(spec.statics).forEach(function (prop) {
Store[prop] = spec.statics[prop];
});
}
Store.storeName = spec.storeName;
Store.handlers = spec.handlers;

Object.keys(spec).forEach(function (prop) {
if (-1 !== IGNORE_ON_PROTOTYPE.indexOf(prop)) {
return;
}
Store.prototype[prop] = spec[prop];
});

return Store;
};

0 comments on commit 44ff5ef

Please sign in to comment.