Skip to content

Commit

Permalink
fix(reactivity): fix shallowReactive nested ref setting edge cases
Browse files Browse the repository at this point in the history
ref #12688
  • Loading branch information
yyx990803 committed Jul 22, 2022
1 parent ba7dd2c commit 2af751b
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/core/observer/index.ts
Expand Up @@ -191,7 +191,7 @@ export function defineReactive(
} else if (getter) {
// #7981: for accessor properties without setter
return
} else if (isRef(value) && !isRef(newVal)) {
} else if (!shallow && isRef(value) && !isRef(newVal)) {
value.value = newVal
return
} else {
Expand Down
15 changes: 10 additions & 5 deletions test/unit/features/v3/reactivity/readonly.spec.ts
Expand Up @@ -483,7 +483,6 @@ describe('reactivity/readonly', () => {
const ror = readonly(r)
const obj = reactive({ ror })
obj.ror = true

expect(obj.ror).toBe(false)
expect(`Set operation on key "value" failed`).toHaveBeenWarned()
})
Expand All @@ -492,14 +491,20 @@ describe('reactivity/readonly', () => {
const r = ref(false)
const ror = readonly(r)
const obj = reactive({ ror })
try {
obj.ror = ref(true) as unknown as boolean
} catch (e) {}

obj.ror = ref(true) as unknown as boolean
expect(obj.ror).toBe(true)
expect(toRaw(obj).ror).not.toBe(ror) // ref successfully replaced
})

test('setting readonly object to writable nested ref', () => {
const r = ref<any>()
const obj = reactive({ r })
const ro = readonly({})
obj.r = ro
expect(obj.r).toBe(ro)
expect(r.value).toBe(ro)
})

test('compatiblity with classes', () => {
const spy = vi.fn()
class Foo {
Expand Down
16 changes: 15 additions & 1 deletion test/unit/features/v3/reactivity/ref.spec.ts
Expand Up @@ -11,7 +11,8 @@ import {
isReactive,
isShallow,
reactive,
computed
computed,
readonly
} from 'v3'
import { effect } from 'v3/reactivity/effect'

Expand Down Expand Up @@ -404,4 +405,17 @@ describe('reactivity/ref', () => {
b.value = obj
expect(spy2).toBeCalledTimes(1)
})

test('ref should preserve value readonly-ness', () => {
const original = {}
const r = reactive(original)
const rr = readonly(original)
const a = ref(original)

expect(a.value).toBe(r)

a.value = rr
expect(a.value).toBe(rr)
expect(a.value).not.toBe(r)
})
})
13 changes: 13 additions & 0 deletions test/unit/features/v3/reactivity/shallowReactive.spec.ts
Expand Up @@ -3,6 +3,7 @@ import {
isRef,
isShallow,
reactive,
Ref,
ref,
shallowReactive,
shallowReadonly
Expand Down Expand Up @@ -45,6 +46,18 @@ describe('shallowReactive', () => {
expect(foo.bar.value).toBe(123)
})

// #12688
test('should not mutate refs', () => {
const original = ref(123)
const foo = shallowReactive<{ bar: Ref<number> | number }>({
bar: original
})
expect(foo.bar).toBe(original)
foo.bar = 234
expect(foo.bar).toBe(234)
expect(original.value).toBe(123)
})

// @discrepancy no shallow/non-shallow versions from the same source -
// cannot support this without real proxies
// #2843
Expand Down

0 comments on commit 2af751b

Please sign in to comment.