From 1cfe290352456f0faf8319d7e193a4b3a31ef352 Mon Sep 17 00:00:00 2001 From: TheDro Date: Thu, 15 Jul 2021 17:17:13 -0400 Subject: [PATCH] fix(reactivity): call array subclass methods (#3624) fix #2314, close #2315 --- .../__tests__/reactiveArray.spec.ts | 41 +++++++++++++++++++ packages/reactivity/src/baseHandlers.ts | 10 ++--- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/packages/reactivity/__tests__/reactiveArray.spec.ts b/packages/reactivity/__tests__/reactiveArray.spec.ts index da3dc1f1..9402f3c5 100644 --- a/packages/reactivity/__tests__/reactiveArray.spec.ts +++ b/packages/reactivity/__tests__/reactiveArray.spec.ts @@ -171,4 +171,45 @@ describe('reactivity/reactive/Array', () => { expect(original.indexOf(ref)).toBe(1) }) }) + + describe('Array subclasses', () => { + class SubArray extends Array { + lastPushed: undefined | T + lastSearched: undefined | T + + push(item: T) { + this.lastPushed = item + return super.push(item) + } + + indexOf(searchElement: T, fromIndex?: number | undefined): number { + this.lastSearched = searchElement + return super.indexOf(searchElement, fromIndex) + } + } + + test('calls correct mutation method on Array subclass', () => { + const subArray = new SubArray(4, 5, 6) + const observed = reactive(subArray) + + subArray.push(7) + expect(subArray.lastPushed).toBe(7) + observed.push(9) + expect(observed.lastPushed).toBe(9) + }) + + test('calls correct identity-sensitive method on Array subclass', () => { + const subArray = new SubArray(4, 5, 6) + const observed = reactive(subArray) + let index + + index = subArray.indexOf(4) + expect(index).toBe(0) + expect(subArray.lastSearched).toBe(4) + + index = observed.indexOf(6) + expect(index).toBe(2) + expect(observed.lastSearched).toBe(6) + }) + }) }) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index a6db361d..8ce7f756 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -49,17 +49,16 @@ function createArrayInstrumentations() { // instrument identity-sensitive Array methods to account for possible reactive // values ;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => { - const method = Array.prototype[key] as any instrumentations[key] = function(this: unknown[], ...args: unknown[]) { - const arr = toRaw(this) + const arr = toRaw(this) as any for (let i = 0, l = this.length; i < l; i++) { track(arr, TrackOpTypes.GET, i + '') } // we run the method using the original args first (which may be reactive) - const res = method.apply(arr, args) + const res = arr[key](...args) if (res === -1 || res === false) { // if that didn't work, run it again using raw values. - return method.apply(arr, args.map(toRaw)) + return arr[key](...args.map(toRaw)) } else { return res } @@ -68,10 +67,9 @@ function createArrayInstrumentations() { // instrument length-altering mutation methods to avoid length being tracked // which leads to infinite loops in some cases (#2137) ;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => { - const method = Array.prototype[key] as any instrumentations[key] = function(this: unknown[], ...args: unknown[]) { pauseTracking() - const res = method.apply(this, args) + const res = (toRaw(this) as any)[key].apply(this, args) resetTracking() return res }