From 96b5d9cd74159db0f90f85638f6ff4d339c50d11 Mon Sep 17 00:00:00 2001 From: Cuttle Cong Date: Mon, 12 Mar 2018 17:08:01 +0800 Subject: [PATCH] fix: urlSync inheritance bug: side effect on parent --- src/decorator/utils/getStateLifeDecorator.js | 54 ++++++--------- test/decorator-urlSync.test.js | 73 ++++++++++++++++++-- 2 files changed, 91 insertions(+), 36 deletions(-) diff --git a/src/decorator/utils/getStateLifeDecorator.js b/src/decorator/utils/getStateLifeDecorator.js index 800faff..d38e487 100644 --- a/src/decorator/utils/getStateLifeDecorator.js +++ b/src/decorator/utils/getStateLifeDecorator.js @@ -67,18 +67,7 @@ export const assignState = action( ) // const g = {} -function extendsHideProps(target, propKey, value) { - const old = target[propKey] && target[propKey] - if (typeof old === 'object' && old !== null) { - // only saved on top parent - if (!Array.isArray(old)) { - Object.assign(old, value) - } - else { - old.push(value) - } - return - } +function setHideProps(target, propKey, value) { Object.defineProperty(target, propKey, { value, configurable: true, @@ -109,27 +98,31 @@ export default (config = {}, name = 'state-life') => { logger.warn('`' + property + '`' + 'is unobservable,', name, 'would make it to be observable.') descriptor = observable(target, property, descriptor) } - const hideArrPropKey = `__[[${name}_array]]__` - - // Firstly! - // Supports inheritance + const hideArrPropKey = `__[[${name}_hooks]]__` - // const hidePropKey = `__[[${name}_origin_hooks]]__` - // if (!target[hidePropKey] || !target[hidePropKey][property]) { - // extendsHideProps(target, hidePropKey, { - // [property]: hooks - // }) + // NOTE: + // class P { + // @urlSync s = 'a' // } - + // class S extends P { + // @urlSync s = 'a' + // } + // new S() --> should run the S.s hooks, ignore P hooks + // Firstly! if (!target[hideArrPropKey]) { - extendsHideProps(target, hideArrPropKey, []) + setHideProps(target, hideArrPropKey, []) + } + // inheritance + else if (!target.hasOwnProperty(hideArrPropKey)) { + // set to Prototype + setHideProps(target, hideArrPropKey, target[hideArrPropKey].slice()) } let i = target[hideArrPropKey].findIndex(([p]) => p === property) if (i >= 0) { target[hideArrPropKey].splice(i, 1) } const func = ( - function () { + function (options, urlKey, property) { let dispose let syncUrlTimer let syncUrlFn @@ -211,10 +204,9 @@ export default (config = {}, name = 'state-life') => { } } } - )() - extendsHideProps(target, hideArrPropKey, [property, func]) - - const arrays = target[hideArrPropKey] + )(options, urlKey, property) + // const arrays = + target[hideArrPropKey].push([property, urlKey, func]) const hooks = { init: target[initKey], @@ -227,14 +219,14 @@ export default (config = {}, name = 'state-life') => { } target[initKey] = function (...args) { - arrays.forEach(([, { init }]) => { + this[hideArrPropKey].forEach(([, , { init }]) => { init.call(this) }) return callHook(this, initKey, args) } if (updateKey) { target[updateKey] = function (...args) { - arrays.forEach(([, { update }]) => { + this[hideArrPropKey].forEach(([, , { update }]) => { update.call(this) }) return callHook(this, updateKey, args) @@ -242,7 +234,7 @@ export default (config = {}, name = 'state-life') => { } target[exitKey] = function (...args) { // @todo setTimeout move to all for better performance. - arrays.forEach(([, { init }]) => { + this[hideArrPropKey].forEach(([, , { init }]) => { init.call(this) }) return callHook(this, exitKey, args) diff --git a/test/decorator-urlSync.test.js b/test/decorator-urlSync.test.js index c2b0e2e..e1c6acc 100644 --- a/test/decorator-urlSync.test.js +++ b/test/decorator-urlSync.test.js @@ -194,11 +194,6 @@ describe('decorator-urlSync', function () { expect(new S().v).toBe('sv') - // expect( - // Object.keys(s['__[[urlsync_origin_hooks]]__']) - // ).toEqual( - // ['str', 'num', 'int', 'obj', 'arr', 'root', 'a', 'b'] - // ) vm = S.create({ a: 'abc', b: 'bbb' }) ReactDOM.render( , @@ -228,4 +223,72 @@ describe('decorator-urlSync', function () { expect(updated).toBe(true) done() }) + + test('multi Son extends alone Parent', async (done) => { + class Parent extends App { + @urlSync + @observable v = '' + } + + class S extends Parent { + @urlSync('s') + @observable v = '' + + @urlSync + @observable sonVal = 'son' + } + class T extends Parent { + @urlSync('t') + @observable v = '' + } + + const s = S.create() + const s2 = S.create() + const t = T.create() + expect( + T.prototype['__mobxLazyInitializers'] + ).not.toBe( + S.prototype['__mobxLazyInitializers'] + ) + const key = '__[[urlsync_hooks]]__' + expect( + T.prototype[key] + ).not.toBe( + S.prototype[key] + ) + + const View = () => ( + h.div({}, + h(s), + h(s2, { key: 'x' }), + h(t) + ) + ) + ReactDOM.render( + , + dom + ) + expect( + parse(hashHistory.getCurrentLocation().search) + ).toEqual({}) + + expect(s.v).toBe('') + t.v = 'ttt' + await mockDelay() + expect( + parse(hashHistory.getCurrentLocation().search) + ).toEqual({ t: 'ttt' }) + expect(t.v).toBe('ttt') + expect(s.v).toBe('') + expect(s2.v).toBe('') + + s.v = 'sss' + s2.v = 'sssWorks' + expect(t.v).toBe('ttt') + await mockDelay() + expect( + parse(hashHistory.getCurrentLocation().search) + ).toEqual({ s: 'sssWorks', t: 'ttt' }) + done() + }) })