fix(reactivity): Map/Set identity methods should work even if raw value contains reactive entries
fix #799
This commit is contained in:
parent
16f9e63951
commit
cc69fd72e3
@ -348,5 +348,19 @@ describe('reactivity/collections', () => {
|
|||||||
map.set('foo', NaN)
|
map.set('foo', NaN)
|
||||||
expect(mapSpy).toHaveBeenCalledTimes(1)
|
expect(mapSpy).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should work with reactive keys in raw map', () => {
|
||||||
|
const raw = new Map()
|
||||||
|
const key = reactive({})
|
||||||
|
raw.set(key, 1)
|
||||||
|
const map = reactive(raw)
|
||||||
|
|
||||||
|
expect(map.has(key)).toBe(true)
|
||||||
|
expect(map.get(key)).toBe(1)
|
||||||
|
|
||||||
|
expect(map.delete(key)).toBe(true)
|
||||||
|
expect(map.has(key)).toBe(false)
|
||||||
|
expect(map.get(key)).toBeUndefined()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -368,5 +368,17 @@ describe('reactivity/collections', () => {
|
|||||||
})
|
})
|
||||||
expect(dummy).toBe(2)
|
expect(dummy).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should work with reactive entries in raw set', () => {
|
||||||
|
const raw = new Set()
|
||||||
|
const entry = reactive({})
|
||||||
|
raw.add(entry)
|
||||||
|
const set = reactive(raw)
|
||||||
|
|
||||||
|
expect(set.has(entry)).toBe(true)
|
||||||
|
|
||||||
|
expect(set.delete(entry)).toBe(true)
|
||||||
|
expect(set.has(entry)).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -67,6 +67,14 @@ describe('reactivity/reactive/Array', () => {
|
|||||||
expect(arr.lastIndexOf(observed, 1)).toBe(-1)
|
expect(arr.lastIndexOf(observed, 1)).toBe(-1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Array identity methods should work if raw value contains reactive objects', () => {
|
||||||
|
const raw = []
|
||||||
|
const obj = reactive({})
|
||||||
|
raw.push(obj)
|
||||||
|
const arr = reactive(raw)
|
||||||
|
expect(arr.includes(obj)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
test('Array identity methods should be reactive', () => {
|
test('Array identity methods should be reactive', () => {
|
||||||
const obj = {}
|
const obj = {}
|
||||||
const arr = reactive([obj, {}])
|
const arr = reactive([obj, {}])
|
||||||
|
@ -23,7 +23,14 @@ const arrayInstrumentations: Record<string, Function> = {}
|
|||||||
for (let i = 0, l = (this as any).length; i < l; i++) {
|
for (let i = 0, l = (this as any).length; i < l; i++) {
|
||||||
track(arr, TrackOpTypes.GET, i + '')
|
track(arr, TrackOpTypes.GET, i + '')
|
||||||
}
|
}
|
||||||
|
// we run the method using the orignal args first (which may be reactive)
|
||||||
|
const res = arr[key](...args)
|
||||||
|
if (res === -1 || res === false) {
|
||||||
|
// if that didn't work, run it again using raw values.
|
||||||
return arr[key](...args.map(toRaw))
|
return arr[key](...args.map(toRaw))
|
||||||
|
} else {
|
||||||
|
return res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -26,16 +26,22 @@ function get(
|
|||||||
wrap: typeof toReactive | typeof toReadonly
|
wrap: typeof toReactive | typeof toReadonly
|
||||||
) {
|
) {
|
||||||
target = toRaw(target)
|
target = toRaw(target)
|
||||||
key = toRaw(key)
|
const rawKey = toRaw(key)
|
||||||
track(target, TrackOpTypes.GET, key)
|
track(target, TrackOpTypes.GET, rawKey)
|
||||||
return wrap(getProto(target).get.call(target, key))
|
const { has, get } = getProto(target)
|
||||||
|
if (has.call(target, key)) {
|
||||||
|
return wrap(get.call(target, key))
|
||||||
|
} else if (has.call(target, rawKey)) {
|
||||||
|
return wrap(get.call(target, rawKey))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function has(this: CollectionTypes, key: unknown): boolean {
|
function has(this: CollectionTypes, key: unknown): boolean {
|
||||||
const target = toRaw(this)
|
const target = toRaw(this)
|
||||||
key = toRaw(key)
|
const rawKey = toRaw(key)
|
||||||
track(target, TrackOpTypes.HAS, key)
|
track(target, TrackOpTypes.HAS, rawKey)
|
||||||
return getProto(target).has.call(target, key)
|
const has = getProto(target).has
|
||||||
|
return has.call(target, key) || has.call(target, rawKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
function size(target: IterableCollections) {
|
function size(target: IterableCollections) {
|
||||||
@ -73,13 +79,16 @@ function set(this: MapTypes, key: unknown, value: unknown) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteEntry(this: CollectionTypes, key: unknown) {
|
function deleteEntry(this: CollectionTypes, key: unknown) {
|
||||||
key = toRaw(key)
|
|
||||||
const target = toRaw(this)
|
const target = toRaw(this)
|
||||||
const proto = getProto(target)
|
const { has, get, delete: del } = getProto(target)
|
||||||
const hadKey = proto.has.call(target, key)
|
let hadKey = has.call(target, key)
|
||||||
const oldValue = proto.get ? proto.get.call(target, key) : undefined
|
if (!hadKey) {
|
||||||
|
key = toRaw(key)
|
||||||
|
hadKey = has.call(target, key)
|
||||||
|
}
|
||||||
|
const oldValue = get ? get.call(target, key) : undefined
|
||||||
// forward the operation before queueing reactions
|
// forward the operation before queueing reactions
|
||||||
const result = proto.delete.call(target, key)
|
const result = del.call(target, key)
|
||||||
if (hadKey) {
|
if (hadKey) {
|
||||||
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
|
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
|
||||||
}
|
}
|
||||||
@ -177,7 +186,7 @@ const mutableInstrumentations: Record<string, Function> = {
|
|||||||
return get(this, key, toReactive)
|
return get(this, key, toReactive)
|
||||||
},
|
},
|
||||||
get size() {
|
get size() {
|
||||||
return size(this as unknown as IterableCollections)
|
return size((this as unknown) as IterableCollections)
|
||||||
},
|
},
|
||||||
has,
|
has,
|
||||||
add,
|
add,
|
||||||
|
Loading…
Reference in New Issue
Block a user