From 47547ed0abfd9c95c7a56c53ca207d22e5f966f0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 25 Nov 2017 13:55:20 -0500 Subject: [PATCH] better implementation of computed properties --- store.js | 102 +++++++++++++++++++------------------------- test/store/index.js | 13 +++++- 2 files changed, 55 insertions(+), 60 deletions(-) diff --git a/store.js b/store.js index e285439ddf..2c573b247f 100644 --- a/store.js +++ b/store.js @@ -12,16 +12,10 @@ function Store(state) { this._changeHandlers = []; this._dependents = []; - this._proto = blankObject(); - this._changed = blankObject(); - this._dependentProps = blankObject(); - this._dirty = blankObject(); - this._state = Object.create(this._proto); - - for (var key in state) { - this._changed[key] = true; - this._state[key] = state[key]; - } + this._computed = blankObject(); + this._sortedComputedProperties = []; + + this._state = assign({}, state); } assign(Store.prototype, { @@ -35,17 +29,6 @@ assign(Store.prototype, { }); }, - _makeDirty: function(prop) { - var dependentProps = this._dependentProps[prop]; - if (dependentProps) { - for (var i = 0; i < dependentProps.length; i += 1) { - var dependentProp = dependentProps[i]; - this._dirty[dependentProp] = this._changed[dependentProp] = true; - this._makeDirty(dependentProp); - } - } - }, - _init: function(props) { var state = {}; for (let i = 0; i < props.length; i += 1) { @@ -65,51 +48,51 @@ assign(Store.prototype, { } }, - compute: function(key, deps, fn) { - var store = this; - var value; + _sortComputedProperties() { + var computed = this._computed; + var sorted = this._sortedComputedProperties = []; + var visited = blankObject(); - store._dirty[key] = true; + function visit(key) { + if (visited[key]) return; + var c = computed[key]; - for (var i = 0; i < deps.length; i += 1) { - var dep = deps[i]; - if (!this._dependentProps[dep]) this._dependentProps[dep] = []; - this._dependentProps[dep].push(key); + if (c) { + c.deps.forEach(visit); + sorted.push(c); + } } - Object.defineProperty(this._proto, key, { - enumerable: true, - get: function() { - if (store._dirty[key]) { - var values = deps.map(function(dep) { - if (dep in store._changed) changed = true; - return store._state[dep]; - }); + for (var key in this._computed) visit(key); + }, - var newValue = fn.apply(null, values); + compute: function(key, deps, fn) { + var store = this; + var value; + + var c = { + deps: deps, + update: function(state, changed, dirty) { + var values = deps.map(function(dep) { + if (dep in changed) dirty = true; + return state[dep]; + }); + if (dirty) { + var newValue = fn.apply(null, values); if (differs(newValue, value)) { value = newValue; - store._changed[key] = true; - - var dependentProps = store._dependentProps[key]; - if (dependentProps) { - for (var i = 0; i < dependentProps.length; i += 1) { - var prop = dependentProps[i]; - store._dirty[prop] = store._changed[prop] = true; - } - } + changed[key] = true; + state[key] = value; } - - store._dirty[key] = false; } - - return value; - }, - set: function() { - throw new Error(`'${key}' is a read-only property`); } - }); + }; + + c.update(this._state, {}, true); + + this._computed[key] = c; + this._sortComputedProperties(); }, onchange: function(callback) { @@ -128,13 +111,16 @@ assign(Store.prototype, { dirty = false; for (var key in newState) { + if (this._computed[key]) throw new Error("'" + key + "' is a read-only property"); if (differs(newState[key], oldState[key])) changed[key] = dirty = true; } if (!dirty) return; - this._state = assign(Object.create(this._proto), oldState, newState); + this._state = assign({}, oldState, newState); - for (var key in changed) this._makeDirty(key); + for (var i = 0; i < this._sortedComputedProperties.length; i += 1) { + this._sortedComputedProperties[i].update(this._state, changed); + } for (var i = 0; i < this._changeHandlers.length; i += 1) { this._changeHandlers[i](this._state, changed); @@ -177,8 +163,6 @@ function combineStores(store, children) { }); } - console.log('updates', updates); - store.set(updates); return store; } diff --git a/test/store/index.js b/test/store/index.js index 258bc01c7c..2f5666152c 100644 --- a/test/store/index.js +++ b/test/store/index.js @@ -1,7 +1,7 @@ import assert from 'assert'; import { Store, combineStores } from '../../store.js'; -describe('store', () => { +describe.only('store', () => { describe('get', () => { it('gets a specific key', () => { const store = new Store({ @@ -177,6 +177,17 @@ describe('store', () => { }, total: 10 }); + + const values = []; + + c.observe('total', total => { + values.push(total); + }); + + a.set({ x: 2, y: 3 }); + b.set({ x: 5, y: 6 }); + + assert.deepEqual(values, [10, 12, 16]); }); }); });