Skip to content

Commit ed43810

Browse files
committed
fix(reactivity): readonly+reactive collection should also expose readonly+reactive values
fix #1772
1 parent 48576e5 commit ed43810

File tree

2 files changed

+54
-12
lines changed

2 files changed

+54
-12
lines changed

packages/reactivity/__tests__/readonly.spec.ts

+38
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,22 @@ describe('reactivity/readonly', () => {
205205
).toHaveBeenWarned()
206206
})
207207

208+
// #1772
209+
test('readonly + reactive should make get() value also readonly + reactive', () => {
210+
const map = reactive(new Collection())
211+
const roMap = readonly(map)
212+
const key = {}
213+
map.set(key, {})
214+
215+
const item = map.get(key)
216+
expect(isReactive(item)).toBe(true)
217+
expect(isReadonly(item)).toBe(false)
218+
219+
const roItem = roMap.get(key)
220+
expect(isReactive(roItem)).toBe(true)
221+
expect(isReadonly(roItem)).toBe(true)
222+
})
223+
208224
if (Collection === Map) {
209225
test('should retrieve readonly values on iteration', () => {
210226
const key1 = {}
@@ -223,6 +239,28 @@ describe('reactivity/readonly', () => {
223239
expect(isReadonly(value)).toBe(true)
224240
}
225241
})
242+
243+
test('should retrieve reactive + readonly values on iteration', () => {
244+
const key1 = {}
245+
const key2 = {}
246+
const original = reactive(new Collection([[key1, {}], [key2, {}]]))
247+
const wrapped: any = readonly(original)
248+
expect(wrapped.size).toBe(2)
249+
for (const [key, value] of wrapped) {
250+
expect(isReadonly(key)).toBe(true)
251+
expect(isReadonly(value)).toBe(true)
252+
expect(isReactive(key)).toBe(true)
253+
expect(isReactive(value)).toBe(true)
254+
}
255+
wrapped.forEach((value: any) => {
256+
expect(isReadonly(value)).toBe(true)
257+
expect(isReactive(value)).toBe(true)
258+
})
259+
for (const value of wrapped.values()) {
260+
expect(isReadonly(value)).toBe(true)
261+
expect(isReactive(value)).toBe(true)
262+
}
263+
})
226264
}
227265
})
228266
})

packages/reactivity/src/collectionHandlers.ts

+16-12
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,20 @@ function get(
3232
key: unknown,
3333
wrap: typeof toReactive | typeof toReadonly | typeof toShallow
3434
) {
35-
target = toRaw(target)
35+
// #1772: readonly(reactive(Map)) should return readonly + reactive version
36+
// of the value
37+
target = (target as any)[ReactiveFlags.RAW]
38+
const rawTarget = toRaw(target)
3639
const rawKey = toRaw(key)
3740
if (key !== rawKey) {
38-
track(target, TrackOpTypes.GET, key)
41+
track(rawTarget, TrackOpTypes.GET, key)
3942
}
40-
track(target, TrackOpTypes.GET, rawKey)
41-
const { has, get } = getProto(target)
42-
if (has.call(target, key)) {
43-
return wrap(get.call(target, key))
44-
} else if (has.call(target, rawKey)) {
45-
return wrap(get.call(target, rawKey))
43+
track(rawTarget, TrackOpTypes.GET, rawKey)
44+
const { has } = getProto(rawTarget)
45+
if (has.call(rawTarget, key)) {
46+
return wrap(target.get(key))
47+
} else if (has.call(rawTarget, rawKey)) {
48+
return wrap(target.get(rawKey))
4649
}
4750
}
4851

@@ -176,15 +179,16 @@ function createIterableMethod(
176179
this: IterableCollections,
177180
...args: unknown[]
178181
): Iterable & Iterator {
179-
const target = toRaw(this)
180-
const isMap = target instanceof Map
182+
const target = (this as any)[ReactiveFlags.RAW]
183+
const rawTarget = toRaw(this)
184+
const isMap = rawTarget instanceof Map
181185
const isPair = method === 'entries' || (method === Symbol.iterator && isMap)
182186
const isKeyOnly = method === 'keys' && isMap
183-
const innerIterator = getProto(target)[method].apply(target, args)
187+
const innerIterator = target[method](...args)
184188
const wrap = isReadonly ? toReadonly : shallow ? toShallow : toReactive
185189
!isReadonly &&
186190
track(
187-
target,
191+
rawTarget,
188192
TrackOpTypes.ITERATE,
189193
isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY
190194
)

0 commit comments

Comments
 (0)