From ed4381020fcea0494f19f11bebabd9108f2dafd7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 6 Aug 2020 11:18:16 -0400 Subject: [PATCH] fix(reactivity): readonly+reactive collection should also expose readonly+reactive values fix #1772 --- .../reactivity/__tests__/readonly.spec.ts | 38 +++++++++++++++++++ packages/reactivity/src/collectionHandlers.ts | 28 ++++++++------ 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/packages/reactivity/__tests__/readonly.spec.ts b/packages/reactivity/__tests__/readonly.spec.ts index e5eb52d5..af42c967 100644 --- a/packages/reactivity/__tests__/readonly.spec.ts +++ b/packages/reactivity/__tests__/readonly.spec.ts @@ -205,6 +205,22 @@ describe('reactivity/readonly', () => { ).toHaveBeenWarned() }) + // #1772 + test('readonly + reactive should make get() value also readonly + reactive', () => { + const map = reactive(new Collection()) + const roMap = readonly(map) + const key = {} + map.set(key, {}) + + const item = map.get(key) + expect(isReactive(item)).toBe(true) + expect(isReadonly(item)).toBe(false) + + const roItem = roMap.get(key) + expect(isReactive(roItem)).toBe(true) + expect(isReadonly(roItem)).toBe(true) + }) + if (Collection === Map) { test('should retrieve readonly values on iteration', () => { const key1 = {} @@ -223,6 +239,28 @@ describe('reactivity/readonly', () => { expect(isReadonly(value)).toBe(true) } }) + + test('should retrieve reactive + readonly values on iteration', () => { + const key1 = {} + const key2 = {} + const original = reactive(new Collection([[key1, {}], [key2, {}]])) + const wrapped: any = readonly(original) + expect(wrapped.size).toBe(2) + for (const [key, value] of wrapped) { + expect(isReadonly(key)).toBe(true) + expect(isReadonly(value)).toBe(true) + expect(isReactive(key)).toBe(true) + expect(isReactive(value)).toBe(true) + } + wrapped.forEach((value: any) => { + expect(isReadonly(value)).toBe(true) + expect(isReactive(value)).toBe(true) + }) + for (const value of wrapped.values()) { + expect(isReadonly(value)).toBe(true) + expect(isReactive(value)).toBe(true) + } + }) } }) }) diff --git a/packages/reactivity/src/collectionHandlers.ts b/packages/reactivity/src/collectionHandlers.ts index 4d6cd99c..0aa93d06 100644 --- a/packages/reactivity/src/collectionHandlers.ts +++ b/packages/reactivity/src/collectionHandlers.ts @@ -32,17 +32,20 @@ function get( key: unknown, wrap: typeof toReactive | typeof toReadonly | typeof toShallow ) { - target = toRaw(target) + // #1772: readonly(reactive(Map)) should return readonly + reactive version + // of the value + target = (target as any)[ReactiveFlags.RAW] + const rawTarget = toRaw(target) const rawKey = toRaw(key) if (key !== rawKey) { - track(target, TrackOpTypes.GET, key) + track(rawTarget, TrackOpTypes.GET, key) } - track(target, TrackOpTypes.GET, rawKey) - 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)) + track(rawTarget, TrackOpTypes.GET, rawKey) + const { has } = getProto(rawTarget) + if (has.call(rawTarget, key)) { + return wrap(target.get(key)) + } else if (has.call(rawTarget, rawKey)) { + return wrap(target.get(rawKey)) } } @@ -176,15 +179,16 @@ function createIterableMethod( this: IterableCollections, ...args: unknown[] ): Iterable & Iterator { - const target = toRaw(this) - const isMap = target instanceof Map + const target = (this as any)[ReactiveFlags.RAW] + const rawTarget = toRaw(this) + const isMap = rawTarget instanceof Map const isPair = method === 'entries' || (method === Symbol.iterator && isMap) const isKeyOnly = method === 'keys' && isMap - const innerIterator = getProto(target)[method].apply(target, args) + const innerIterator = target[method](...args) const wrap = isReadonly ? toReadonly : shallow ? toShallow : toReactive !isReadonly && track( - target, + rawTarget, TrackOpTypes.ITERATE, isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY )