Skip to content

Commit

Permalink
fix(reactivity): accept subtypes of collections (#1864)
Browse files Browse the repository at this point in the history
  • Loading branch information
unbyte authored Aug 17, 2020
1 parent 6ccd9ac commit d005b57
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 13 deletions.
18 changes: 18 additions & 0 deletions packages/reactivity/__tests__/reactive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@ describe('reactivity/reactive', () => {
expect(isReactive(observed.array[0])).toBe(true)
})

test('process subtypes of collections properly', () => {
class CustomMap extends Map {
count = 0

set(key: any, value: any): this {
super.set(key, value)
this.count++
return this
}
}

const testMap = new CustomMap()
const observed = reactive(testMap)
expect(observed.count).toBe(0)
observed.set('test', 'value')
expect(observed.count).toBe(1)
})

test('observed value should proxy mutations to original (Object)', () => {
const original: any = { foo: 1 }
const observed = reactive(original)
Expand Down
41 changes: 28 additions & 13 deletions packages/reactivity/src/reactive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isObject, toRawType, def, hasOwn, makeMap } from '@vue/shared'
import { isObject, toRawType, def, hasOwn } from '@vue/shared'
import {
mutableHandlers,
readonlyHandlers,
Expand Down Expand Up @@ -30,17 +30,31 @@ export interface Target {
[ReactiveFlags.READONLY]?: any
}

const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
const isObservableType = /*#__PURE__*/ makeMap(
'Object,Array,Map,Set,WeakMap,WeakSet'
)
const enum TargetType {
INVALID = 0,
COMMON = 1,
COLLECTION = 2
}

const canObserve = (value: Target): boolean => {
return (
!value[ReactiveFlags.SKIP] &&
isObservableType(toRawType(value)) &&
Object.isExtensible(value)
)
function targetTypeMap(rawType: string) {
switch (rawType) {
case 'Object':
case 'Array':
return TargetType.COMMON
case 'Map':
case 'Set':
case 'WeakMap':
case 'WeakSet':
return TargetType.COLLECTION
default:
return TargetType.INVALID
}
}

function getTargetType(value: Target) {
return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
? TargetType.INVALID
: targetTypeMap(toRawType(value))
}

// only unwrap nested ref
Expand Down Expand Up @@ -148,12 +162,13 @@ function createReactiveObject(
return target[reactiveFlag]
}
// only a whitelist of value types can be observed.
if (!canObserve(target)) {
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
const observed = new Proxy(
target,
collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
def(target, reactiveFlag, observed)
return observed
Expand Down

0 comments on commit d005b57

Please sign in to comment.