fix(reactivity): call array subclass methods (#3624)

fix #2314, close #2315
This commit is contained in:
TheDro 2021-07-15 17:17:13 -04:00 committed by GitHub
parent 299f7c08c7
commit 1cfe290352
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 6 deletions

View File

@ -171,4 +171,45 @@ describe('reactivity/reactive/Array', () => {
expect(original.indexOf(ref)).toBe(1) expect(original.indexOf(ref)).toBe(1)
}) })
}) })
describe('Array subclasses', () => {
class SubArray<T> extends Array<T> {
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)
})
})
}) })

View File

@ -49,17 +49,16 @@ function createArrayInstrumentations() {
// instrument identity-sensitive Array methods to account for possible reactive // instrument identity-sensitive Array methods to account for possible reactive
// values // values
;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => { ;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
const method = Array.prototype[key] as any
instrumentations[key] = function(this: unknown[], ...args: unknown[]) { 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++) { for (let i = 0, l = this.length; i < l; i++) {
track(arr, TrackOpTypes.GET, i + '') track(arr, TrackOpTypes.GET, i + '')
} }
// we run the method using the original args first (which may be reactive) // 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 (res === -1 || res === false) {
// if that didn't work, run it again using raw values. // 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 { } else {
return res return res
} }
@ -68,10 +67,9 @@ function createArrayInstrumentations() {
// instrument length-altering mutation methods to avoid length being tracked // instrument length-altering mutation methods to avoid length being tracked
// which leads to infinite loops in some cases (#2137) // which leads to infinite loops in some cases (#2137)
;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => { ;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
const method = Array.prototype[key] as any
instrumentations[key] = function(this: unknown[], ...args: unknown[]) { instrumentations[key] = function(this: unknown[], ...args: unknown[]) {
pauseTracking() pauseTracking()
const res = method.apply(this, args) const res = (toRaw(this) as any)[key].apply(this, args)
resetTracking() resetTracking()
return res return res
} }