From 9ca3f24875d2220bfdbdf58bee418ba4aade0944 Mon Sep 17 00:00:00 2001 From: Leah Date: Sat, 10 Mar 2018 02:55:01 +0100 Subject: [PATCH] Fix an issue with `getRoot` and optimise cache --- src/index.js | 77 +++++++++++++++++++++++++--------------------- test/model.test.js | 48 +++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 35 deletions(-) diff --git a/src/index.js b/src/index.js index bd7ff02..8ff17f7 100755 --- a/src/index.js +++ b/src/index.js @@ -11,28 +11,29 @@ const objKeys = Object.keys.bind(Object); function diff(oldObj, newObj, whitelist) { if (oldObj === newObj) return oldObj; - const keys = objKeys(oldObj).reduce( - (acc, val) => (~acc.indexOf(val) ? acc : acc.concat(val)), - objKeys(newObj) - ); - keys.forEach(key => { - if (~whitelist.indexOf(key)) return; - const oldVal = oldObj[key]; - const newVal = newObj[key]; - if (oldVal === newVal) return oldObj; - if ( - oldVal != null && - newVal != null && - typeof oldVal === 'object' && - typeof newVal === 'object' - ) { - oldVal[modelSymbol] - ? oldVal.applySnapshot(newVal, true) - : diff(oldVal, newVal, whitelist); - } else { - oldObj[apc][key] = newVal; - } - }); + objKeys(oldObj) + .reduce( + (acc, val) => (~acc.indexOf(val) ? acc : acc.concat(val)), + objKeys(newObj) + ) + .forEach(key => { + if (~whitelist.indexOf(key)) return; + const oldVal = oldObj[key]; + const newVal = newObj[key]; + if (oldVal === newVal) return oldObj; + if ( + oldVal != null && + newVal != null && + typeof oldVal === 'object' && + typeof newVal === 'object' + ) { + oldVal[modelSymbol] + ? oldVal.applySnapshot(newVal, true) + : diff(oldVal, newVal, whitelist); + } else { + oldObj[apc][key] = newVal; + } + }); } function setUpObject(obj, emit, symbol, path, parent = obj) { @@ -158,7 +159,7 @@ const model = (name, { initial, actions, views }) => { return parent; }, getRoot() { - return parent ? parent.getRoot() === null && parent : null; + return parent ? (parent.getRoot() ? parent.getRoot() : parent) : null; }, }; @@ -176,11 +177,8 @@ const model = (name, { initial, actions, views }) => { state = setUpObject(state, emit, symbol, ''); - const viewProxy = state[vpc]; - const actionProxy = state[apc]; - if (actions) { - const boundActions = actions(actionProxy); + const boundActions = actions(state[apc]); const keys = objKeys(boundActions); const emitSnapshot = (name, args) => { @@ -207,28 +205,37 @@ const model = (name, { initial, actions, views }) => { } if (views) { - const boundViews = views(viewProxy); + const boundViews = views(state[vpc]); const keys = objKeys(boundViews); - const cache = {}; + // r = refresh on get, v = value + // I don't trust uglify here + const caches = {}; for (let key of keys) { - const viewFn = boundViews[key]; - Object.defineProperty(state, key, { - get: () => cache[key], + get: () => { + const cache = caches[key]; + if (cache.r) { + cache.r = false; + cache.v = boundViews[key](); + } + return cache.v; + }, configurable: false, }); - cache[key] = viewFn(); + caches[key] = { v: boundViews[key](), r: false }; } // update the cache on change - emitter.on('patch', () => { + state.onPatch(() => { for (let key of keys) { - cache[key] = boundViews[key](); + caches[key].r = true; } }); } + // typeof state.init === 'function' && state.init(); + return state; }; diff --git a/test/model.test.js b/test/model.test.js index 0d4014d..d46531f 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -183,4 +183,52 @@ describe('model()', function() { expect(instance.fullname).toBe('Tom Clancy'); }); + + it('should get the root in nested models', () => { + const Model2 = model('M1', { + initial: () => ({}), + actions: self => ({ + root() { + return self.getRoot().thing; + }, + }), + }); + + const Model1 = model('M1', { + initial: () => ({ + nest: Model2(), + }), + actions: self => ({ + root() { + return self.getRoot().thing; + }, + }), + }); + + const Model = model('M', { + initial: () => ({ + nest: Model1(), + thing: 'root', + }), + }); + + const instance = Model(); + + debugger; + expect(instance.nest.root()).toBe('root'); + expect(instance.nest.nest.root()).toBe('root'); + }); + + // it('should call init when instantiated', () => { + // const init = jest.fn(); + // + // const Model = model('Model', { + // initial: () => ({}), + // actions: state => ({ + // init, + // }), + // }); + // Model(); + // expect(init).toBeCalled(); + // }); });