Skip to content

Commit

Permalink
fix(reactivity): fix mutation on user proxy of reactive Array
Browse files Browse the repository at this point in the history
close #9742
close #9751
close #9750
  • Loading branch information
yyx990803 committed Dec 7, 2023
1 parent 983d45d commit 6ecbd5c
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 17 deletions.
30 changes: 26 additions & 4 deletions packages/reactivity/__tests__/reactive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,21 @@ describe('reactivity/reactive', () => {
expect(original.bar).toBe(original2)
})

// #1246
test('mutation on objects using reactive as prototype should not trigger', () => {
const observed = reactive({ foo: 1 })
const original = Object.create(observed)
let dummy
effect(() => (dummy = original.foo))
expect(dummy).toBe(1)
observed.foo = 2
expect(dummy).toBe(2)
original.foo = 3
expect(dummy).toBe(2)
original.foo = 4
expect(dummy).toBe(2)
})

test('toRaw', () => {
const original = { foo: 1 }
const observed = reactive(original)
Expand All @@ -166,11 +181,18 @@ describe('reactivity/reactive', () => {
})

test('toRaw on object using reactive as prototype', () => {
const original = reactive({})
const obj = Object.create(original)
const original = { foo: 1 }
const observed = reactive(original)
const inherted = Object.create(observed)
expect(toRaw(inherted)).toBe(inherted)
})

test('toRaw on user Proxy wrapping reactive', () => {
const original = {}
const re = reactive(original)
const obj = new Proxy(re, {})
const raw = toRaw(obj)
expect(raw).toBe(obj)
expect(raw).not.toBe(toRaw(original))
expect(raw).toBe(original)
})

test('should not unwrap Ref<T>', () => {
Expand Down
9 changes: 9 additions & 0 deletions packages/reactivity/__tests__/reactiveArray.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ describe('reactivity/reactive/Array', () => {
expect(length).toBe('01')
})

// #9742
test('mutation on user proxy of reactive Array', () => {
const array = reactive<number[]>([])
const proxy = new Proxy(array, {})
proxy.push(1)
expect(array).toHaveLength(1)
expect(proxy).toHaveLength(1)
})

describe('Array methods w/ refs', () => {
let original: any[]
beforeEach(() => {
Expand Down
32 changes: 19 additions & 13 deletions packages/reactivity/src/baseHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,25 @@ class BaseReactiveHandler implements ProxyHandler<Target> {
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
return shallow
} else if (
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target
} else if (key === ReactiveFlags.RAW) {
if (
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target) ||
// receiver is not the reactive proxy, but has the same prototype
// this means the reciever is a user proxy of the reactive proxy

This comment has been minimized.

Copy link
@DragonnZhang

DragonnZhang Dec 7, 2023

typo here, it should be receiver

Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
) {
return target
}
// early return undefined
return
}

const targetIsArray = isArray(target)
Expand Down

0 comments on commit 6ecbd5c

Please sign in to comment.