implement Store

pull/951/head
Rich Harris 7 years ago
parent 14b27b71e1
commit d0e5098ea6

@ -1,5 +1,6 @@
src/shared src/shared
shared.js shared.js
store.js
test/test.js test/test.js
test/setup.js test/setup.js
**/_actual.js **/_actual.js

@ -0,0 +1,87 @@
import {
assign,
blankObject,
differs,
dispatchObservers,
get,
observe
} from './shared.js';
function Store(state) {
this._state = state ? assign({}, state) : {};
this._observers = { pre: blankObject(), post: blankObject() };
this._changeHandlers = [];
this._dependents = [];
}
assign(Store.prototype, {
get,
observe
}, {
_add: function(component, props) {
this._dependents.push({
component: component,
props: props
});
},
_remove: function(component) {
let i = this._dependents.length;
while (i--) {
if (this._dependents[i].component === component) {
this._dependents.splice(i, 1);
return;
}
}
},
onchange: function(callback) {
this._changeHandlers.push(callback);
return {
cancel: function() {
var index = this._changeHandlers.indexOf(callback);
if (~index) this._changeHandlers.splice(index, 1);
}
};
},
set: function(newState) {
var oldState = this._state,
changed = {},
dirty = false;
for (var key in newState) {
if (differs(newState[key], oldState[key])) changed[key] = dirty = true;
}
if (!dirty) return;
this._state = assign({}, oldState, newState);
for (var i = 0; i < this._changeHandlers.length; i += 1) {
this._changeHandlers[i](this._state, changed);
}
dispatchObservers(this, this._observers.pre, changed, this._state, oldState);
var dependents = this._dependents.slice(); // guard against mutations
for (var i = 0; i < dependents.length; i += 1) {
var dependent = dependents[i];
var componentState = {};
dirty = false;
for (var j = 0; j < dependent.props.length; j += 1) {
var prop = dependent.props[j];
if (prop in changed) {
componentState['$' + prop] = this._state[prop];
dirty = true;
}
}
if (dirty) dependent.component.set(componentState);
}
dispatchObservers(this, this._observers.post, changed, this._state, oldState);
}
});
export default Store;

@ -0,0 +1,95 @@
import assert from 'assert';
import Store from '../../store.js';
describe('store', () => {
describe('get', () => {
it('gets a specific key', () => {
const store = new Store({
foo: 'bar'
});
assert.equal(store.get('foo'), 'bar');
});
it('gets the entire state object', () => {
const store = new Store({
foo: 'bar'
});
assert.deepEqual(store.get(), { foo: 'bar' });
});
});
describe('set', () => {
it('sets state', () => {
const store = new Store();
store.set({
foo: 'bar'
});
assert.equal(store.get('foo'), 'bar');
});
});
describe('observe', () => {
it('observes state', () => {
let newFoo;
let oldFoo;
const store = new Store({
foo: 'bar'
});
store.observe('foo', (n, o) => {
newFoo = n;
oldFoo = o;
});
assert.equal(newFoo, 'bar');
assert.equal(oldFoo, undefined);
store.set({
foo: 'baz'
});
assert.equal(newFoo, 'baz');
assert.equal(oldFoo, 'bar');
});
});
describe('onchange', () => {
it('fires a callback when state changes', () => {
const store = new Store();
let count = 0;
let args;
store.onchange((state, changed) => {
count += 1;
args = { state, changed };
});
store.set({ foo: 'bar' });
assert.equal(count, 1);
assert.deepEqual(args, {
state: { foo: 'bar' },
changed: { foo: true }
});
// this should be a noop
store.set({ foo: 'bar' });
assert.equal(count, 1);
// this shouldn't
store.set({ foo: 'baz' });
assert.equal(count, 2);
assert.deepEqual(args, {
state: { foo: 'baz' },
changed: { foo: true }
});
});
});
});
Loading…
Cancel
Save