Fix array un-observe

This commit is contained in:
Bartek Fabiszewski 2019-12-14 17:10:19 +01:00
parent c506f8d98e
commit 0039ecdd4f
2 changed files with 81 additions and 16 deletions

View File

@ -81,6 +81,11 @@ export default class uObserve {
} }
if (this.isObserved(obj, property)) { if (this.isObserved(obj, property)) {
obj._values[property] = value; obj._values[property] = value;
if (Array.isArray(obj[property])) {
for (const obs of obj._observers[property]) {
this.observeArray(obj[property], obs);
}
}
} else { } else {
obj[property] = value; obj[property] = value;
} }
@ -110,7 +115,7 @@ export default class uObserve {
uObserve.notify(obj._observers[property], newValue); uObserve.notify(obj._observers[property], newValue);
} }
if (Array.isArray(obj[property])) { if (Array.isArray(obj[property])) {
this.observeArray(obj[property], observer); this.observeArray(obj[property], obj._observers[property]);
} }
} }
}); });
@ -139,21 +144,17 @@ export default class uObserve {
/** /**
* Observe array * Observe array
* @param {Object} arr * @param {Object} arr
* @param {ObserveCallback} observer * @param {(ObserveCallback|Set<ObserveCallback>)} observer
*/ */
static observeArray(arr, observer) { static observeArray(arr, observer) {
this.addObserver(arr, observer); if (observer instanceof Set) {
[ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift' ].forEach( for (const obs of observer) {
(operation) => { this.addObserver(arr, obs);
const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, operation); }
descriptor.value = function () { } else {
const result = Array.prototype[operation].apply(arr, arguments); this.addObserver(arr, observer);
console.log(`[${operation}] ` + (arr.length ? `[${arr[0]}, …](${arr.length})` : arr)); }
uObserve.notify(arr._observers, arr); this.overrideArrayPrototypes(arr, arguments);
return result;
};
Object.defineProperty(arr, operation, descriptor);
});
} }
/** /**
@ -271,11 +272,32 @@ export default class uObserve {
} }
} }
static restoreArrayPrototypes(arr) { /**
* @param {Object} arr
*/
static overrideArrayPrototypes(arr) {
[ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift' ].forEach( [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift' ].forEach(
(operation) => { (operation) => {
const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, operation); const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, operation);
Object.defineProperty(arr, operation, descriptor); if (!arr.hasOwnProperty(operation)) {
descriptor.value = function () {
const result = Array.prototype[operation].apply(arr, arguments);
console.log(`[${operation}] ` + (arr.length ? `[${arr[0]}, …](${arr.length})` : arr));
uObserve.notify(arr._observers, arr);
return result;
};
Object.defineProperty(arr, operation, descriptor);
}
});
}
/**
* @param {Object} arr
*/
static restoreArrayPrototypes(arr) {
[ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift' ].forEach(
(operation) => {
delete arr[operation];
}); });
} }

View File

@ -157,6 +157,8 @@ describe('Observe tests', () => {
it('should retain observers after array is reassigned', () => { it('should retain observers after array is reassigned', () => {
// given // given
let result2 = false;
let resultValue2;
const array = [ 1, 2 ]; const array = [ 1, 2 ];
const newArray = [ 3, 4 ]; const newArray = [ 3, 4 ];
object = { array: array }; object = { array: array };
@ -164,15 +166,56 @@ describe('Observe tests', () => {
result = true; result = true;
resultValue = value; resultValue = value;
}); });
uObserve.observe(object, 'array', (value) => {
result2 = true;
resultValue2 = value;
});
// when // when
object.array = newArray; object.array = newArray;
result = false; result = false;
result2 = false;
expect(result).toBe(false); expect(result).toBe(false);
expect(result2).toBe(false);
object.array.push(5); object.array.push(5);
// then // then
expect(result).toBe(true); expect(result).toBe(true);
expect(result2).toBe(true);
expect(resultValue).toEqual(newArray); expect(resultValue).toEqual(newArray);
// noinspection JSUnusedAssignment
expect(resultValue2).toEqual(newArray);
});
it('should retain observers after array property is silently set', () => {
// given
let result2 = false;
let resultValue2;
const array = [ 1, 2 ];
const newArray = [ 3, 4 ];
object = { array: [] };
uObserve.observe(object, 'array', (value) => {
result = true;
resultValue = value;
});
uObserve.observe(object, 'array', (value) => {
result2 = true;
resultValue2 = value;
});
// when
uObserve.setSilently(object, 'array', array);
object.array = newArray;
result = false;
result2 = false;
expect(result).toBe(false);
expect(result2).toBe(false);
object.array.push(5);
// then
expect(result).toBe(true);
expect(result2).toBe(true);
expect(resultValue).toEqual(newArray);
// noinspection JSUnusedAssignment
expect(resultValue2).toEqual(newArray);
}); });
}); });