Skip to content

Commit 2af751b

Browse files
committed
fix(reactivity): fix shallowReactive nested ref setting edge cases
ref #12688
1 parent ba7dd2c commit 2af751b

File tree

4 files changed

+39
-7
lines changed

4 files changed

+39
-7
lines changed

src/core/observer/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ export function defineReactive(
191191
} else if (getter) {
192192
// #7981: for accessor properties without setter
193193
return
194-
} else if (isRef(value) && !isRef(newVal)) {
194+
} else if (!shallow && isRef(value) && !isRef(newVal)) {
195195
value.value = newVal
196196
return
197197
} else {

test/unit/features/v3/reactivity/readonly.spec.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,6 @@ describe('reactivity/readonly', () => {
483483
const ror = readonly(r)
484484
const obj = reactive({ ror })
485485
obj.ror = true
486-
487486
expect(obj.ror).toBe(false)
488487
expect(`Set operation on key "value" failed`).toHaveBeenWarned()
489488
})
@@ -492,14 +491,20 @@ describe('reactivity/readonly', () => {
492491
const r = ref(false)
493492
const ror = readonly(r)
494493
const obj = reactive({ ror })
495-
try {
496-
obj.ror = ref(true) as unknown as boolean
497-
} catch (e) {}
498-
494+
obj.ror = ref(true) as unknown as boolean
499495
expect(obj.ror).toBe(true)
500496
expect(toRaw(obj).ror).not.toBe(ror) // ref successfully replaced
501497
})
502498

499+
test('setting readonly object to writable nested ref', () => {
500+
const r = ref<any>()
501+
const obj = reactive({ r })
502+
const ro = readonly({})
503+
obj.r = ro
504+
expect(obj.r).toBe(ro)
505+
expect(r.value).toBe(ro)
506+
})
507+
503508
test('compatiblity with classes', () => {
504509
const spy = vi.fn()
505510
class Foo {

test/unit/features/v3/reactivity/ref.spec.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
isReactive,
1212
isShallow,
1313
reactive,
14-
computed
14+
computed,
15+
readonly
1516
} from 'v3'
1617
import { effect } from 'v3/reactivity/effect'
1718

@@ -404,4 +405,17 @@ describe('reactivity/ref', () => {
404405
b.value = obj
405406
expect(spy2).toBeCalledTimes(1)
406407
})
408+
409+
test('ref should preserve value readonly-ness', () => {
410+
const original = {}
411+
const r = reactive(original)
412+
const rr = readonly(original)
413+
const a = ref(original)
414+
415+
expect(a.value).toBe(r)
416+
417+
a.value = rr
418+
expect(a.value).toBe(rr)
419+
expect(a.value).not.toBe(r)
420+
})
407421
})

test/unit/features/v3/reactivity/shallowReactive.spec.ts

+13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
isRef,
44
isShallow,
55
reactive,
6+
Ref,
67
ref,
78
shallowReactive,
89
shallowReadonly
@@ -45,6 +46,18 @@ describe('shallowReactive', () => {
4546
expect(foo.bar.value).toBe(123)
4647
})
4748

49+
// #12688
50+
test('should not mutate refs', () => {
51+
const original = ref(123)
52+
const foo = shallowReactive<{ bar: Ref<number> | number }>({
53+
bar: original
54+
})
55+
expect(foo.bar).toBe(original)
56+
foo.bar = 234
57+
expect(foo.bar).toBe(234)
58+
expect(original.value).toBe(123)
59+
})
60+
4861
// @discrepancy no shallow/non-shallow versions from the same source -
4962
// cannot support this without real proxies
5063
// #2843

0 commit comments

Comments
 (0)