fix(reactivity): ensure that shallow and normal proxies are tracked seperately (close #2843) (#2851)

fix #2843
This commit is contained in:
Thorsten Lünborg 2021-03-26 20:39:56 +01:00 committed by GitHub
parent 68de9f408a
commit 22cc4a7659
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 10 deletions

View File

@ -1,4 +1,5 @@
import { shallowReactive, isReactive, reactive } from '../src/reactive' import { isReactive, reactive, shallowReactive } from '../src/reactive'
import { effect } from '../src/effect' import { effect } from '../src/effect'
describe('shallowReactive', () => { describe('shallowReactive', () => {
@ -13,6 +14,16 @@ describe('shallowReactive', () => {
expect(isReactive(props.n)).toBe(true) expect(isReactive(props.n)).toBe(true)
}) })
// #2843
test('should allow shallow und normal reactive for same target', async () => {
const original = { foo: {} }
const shallowProxy = shallowReactive(original)
const reactiveProxy = reactive(original)
expect(shallowProxy).not.toBe(reactiveProxy)
expect(isReactive(shallowProxy.foo)).toBe(false)
expect(isReactive(reactiveProxy.foo)).toBe(true)
})
describe('collections', () => { describe('collections', () => {
test('should be reactive', () => { test('should be reactive', () => {
const shallowSet = shallowReactive(new Set()) const shallowSet = shallowReactive(new Set())

View File

@ -1,4 +1,4 @@
import { isReactive, isReadonly, shallowReadonly } from '../src' import { isReactive, isReadonly, readonly, shallowReadonly } from '../src'
describe('reactivity/shallowReadonly', () => { describe('reactivity/shallowReadonly', () => {
test('should not make non-reactive properties reactive', () => { test('should not make non-reactive properties reactive', () => {
@ -27,6 +27,16 @@ describe('reactivity/shallowReadonly', () => {
).not.toHaveBeenWarned() ).not.toHaveBeenWarned()
}) })
// #2843
test('should differentiate from normal readonly calls', async () => {
const original = { foo: {} }
const shallowProxy = shallowReadonly(original)
const reactiveProxy = readonly(original)
expect(shallowProxy).not.toBe(reactiveProxy)
expect(isReadonly(shallowProxy.foo)).toBe(false)
expect(isReadonly(reactiveProxy.foo)).toBe(true)
})
describe('collection/Map', () => { describe('collection/Map', () => {
;[Map, WeakMap].forEach(Collection => { ;[Map, WeakMap].forEach(Collection => {
test('should make the map/weak-map readonly', () => { test('should make the map/weak-map readonly', () => {

View File

@ -5,7 +5,9 @@ import {
ReactiveFlags, ReactiveFlags,
Target, Target,
readonlyMap, readonlyMap,
reactiveMap reactiveMap,
shallowReactiveMap,
shallowReadonlyMap
} from './reactive' } from './reactive'
import { TrackOpTypes, TriggerOpTypes } from './operations' import { TrackOpTypes, TriggerOpTypes } from './operations'
import { import {
@ -80,7 +82,15 @@ function createGetter(isReadonly = false, shallow = false) {
return isReadonly return isReadonly
} else if ( } else if (
key === ReactiveFlags.RAW && key === ReactiveFlags.RAW &&
receiver === (isReadonly ? readonlyMap : reactiveMap).get(target) receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) { ) {
return target return target
} }

View File

@ -28,7 +28,9 @@ export interface Target {
} }
export const reactiveMap = new WeakMap<Target, any>() export const reactiveMap = new WeakMap<Target, any>()
export const shallowReactiveMap = new WeakMap<Target, any>()
export const readonlyMap = new WeakMap<Target, any>() export const readonlyMap = new WeakMap<Target, any>()
export const shallowReadonlyMap = new WeakMap<Target, any>()
const enum TargetType { const enum TargetType {
INVALID = 0, INVALID = 0,
@ -92,7 +94,8 @@ export function reactive(target: object) {
target, target,
false, false,
mutableHandlers, mutableHandlers,
mutableCollectionHandlers mutableCollectionHandlers,
reactiveMap
) )
} }
@ -106,7 +109,8 @@ export function shallowReactive<T extends object>(target: T): T {
target, target,
false, false,
shallowReactiveHandlers, shallowReactiveHandlers,
shallowCollectionHandlers shallowCollectionHandlers,
shallowReactiveMap
) )
} }
@ -143,7 +147,8 @@ export function readonly<T extends object>(
target, target,
true, true,
readonlyHandlers, readonlyHandlers,
readonlyCollectionHandlers readonlyCollectionHandlers,
readonlyMap
) )
} }
@ -160,7 +165,8 @@ export function shallowReadonly<T extends object>(
target, target,
true, true,
shallowReadonlyHandlers, shallowReadonlyHandlers,
shallowReadonlyCollectionHandlers shallowReadonlyCollectionHandlers,
shallowReadonlyMap
) )
} }
@ -168,7 +174,8 @@ function createReactiveObject(
target: Target, target: Target,
isReadonly: boolean, isReadonly: boolean,
baseHandlers: ProxyHandler<any>, baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any> collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) { ) {
if (!isObject(target)) { if (!isObject(target)) {
if (__DEV__) { if (__DEV__) {
@ -185,7 +192,6 @@ function createReactiveObject(
return target return target
} }
// target already has corresponding Proxy // target already has corresponding Proxy
const proxyMap = isReadonly ? readonlyMap : reactiveMap
const existingProxy = proxyMap.get(target) const existingProxy = proxyMap.get(target)
if (existingProxy) { if (existingProxy) {
return existingProxy return existingProxy