fix(reactivity): avoid length mutating array methods causing infinite updates (#2138)
fix #2137 Co-authored-by: Evan You <yyx990803@gmail.com>
This commit is contained in:
@@ -8,7 +8,13 @@ import {
|
||||
reactiveMap
|
||||
} from './reactive'
|
||||
import { TrackOpTypes, TriggerOpTypes } from './operations'
|
||||
import { track, trigger, ITERATE_KEY } from './effect'
|
||||
import {
|
||||
track,
|
||||
trigger,
|
||||
ITERATE_KEY,
|
||||
pauseTracking,
|
||||
enableTracking
|
||||
} from './effect'
|
||||
import {
|
||||
isObject,
|
||||
hasOwn,
|
||||
@@ -32,22 +38,36 @@ const readonlyGet = /*#__PURE__*/ createGetter(true)
|
||||
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
|
||||
|
||||
const arrayInstrumentations: Record<string, Function> = {}
|
||||
// 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
|
||||
arrayInstrumentations[key] = function(this: unknown[], ...args: unknown[]) {
|
||||
const arr = toRaw(this)
|
||||
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 = (arr[key] as any)(...args)
|
||||
const res = method.apply(arr, args)
|
||||
if (res === -1 || res === false) {
|
||||
// if that didn't work, run it again using raw values.
|
||||
return (arr[key] as any)(...args.map(toRaw))
|
||||
return method.apply(arr, args.map(toRaw))
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
}
|
||||
})
|
||||
// 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
|
||||
arrayInstrumentations[key] = function(this: unknown[], ...args: unknown[]) {
|
||||
pauseTracking()
|
||||
const res = method.apply(this, args)
|
||||
enableTracking()
|
||||
return res
|
||||
}
|
||||
})
|
||||
|
||||
function createGetter(isReadonly = false, shallow = false) {
|
||||
return function get(target: Target, key: string | symbol, receiver: object) {
|
||||
|
||||
Reference in New Issue
Block a user