mirror of https://github.com/sveltejs/svelte
parent
14b27b71e1
commit
d0e5098ea6
@ -1,5 +1,6 @@
|
||||
src/shared
|
||||
shared.js
|
||||
store.js
|
||||
test/test.js
|
||||
test/setup.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…
Reference in new issue