From 4c95377e0a4fd79f0120d5cfc33ec5f3d8707182 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 5 Jul 2025 00:40:40 +0800 Subject: [PATCH 1/2] fix(reactivity): avoid unwrapping `.value` when the proxy is a direct wrapper of Ref --- packages/reactivity/__tests__/readonly.spec.ts | 2 +- packages/reactivity/__tests__/ref.spec.ts | 4 ++-- packages/reactivity/src/baseHandlers.ts | 7 ++++++- packages/reactivity/src/dep.ts | 6 ------ 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/reactivity/__tests__/readonly.spec.ts b/packages/reactivity/__tests__/readonly.spec.ts index 5c70b6082f1..b035779f85a 100644 --- a/packages/reactivity/__tests__/readonly.spec.ts +++ b/packages/reactivity/__tests__/readonly.spec.ts @@ -523,7 +523,7 @@ describe('reactivity/readonly', () => { }) }) -test.todo('should be able to trigger with triggerRef', () => { +test('should be able to trigger with triggerRef', () => { const r = shallowRef({ a: 1 }) const ror = readonly(r) let dummy diff --git a/packages/reactivity/__tests__/ref.spec.ts b/packages/reactivity/__tests__/ref.spec.ts index 7976a5373ba..6f7f789b5aa 100644 --- a/packages/reactivity/__tests__/ref.spec.ts +++ b/packages/reactivity/__tests__/ref.spec.ts @@ -59,9 +59,9 @@ describe('reactivity/ref', () => { expect(fn).toHaveBeenCalledTimes(2) expect(dummy).toBe(3) - // mutating b.value should trigger the effect twice. (once for a.value change and once for b.value change) + // mutating b.value should only trigger effect once b.value = 5 - expect(fn).toHaveBeenCalledTimes(4) + expect(fn).toHaveBeenCalledTimes(3) expect(dummy).toBe(5) }) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index faec3012f40..abecce6b6bf 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -96,15 +96,20 @@ class BaseReactiveHandler implements ProxyHandler { } } + const wasRef = isRef(target) const res = Reflect.get( target, key, // if this is a proxy wrapping a ref, return methods using the raw ref // as receiver so that we don't have to call `toRaw` on the ref in all // its class methods - isRef(target) ? target : receiver, + wasRef ? target : receiver, ) + if (wasRef) { + return res + } + if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { return res } diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index 3fd81514b6f..184964c17b8 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -15,12 +15,6 @@ class Dep implements Dependency { _subs: Link | undefined = undefined subsTail: Link | undefined = undefined - /** - * @internal - */ - readonly __v_skip = true - // TODO isolatedDeclarations ReactiveFlags.SKIP - constructor( private map: KeyToDepMap, private key: unknown, From 3d12140e5fcbf6f25509f6b7c9fb012bdfe3c8ed Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 6 Jul 2025 21:08:47 +0800 Subject: [PATCH 2/2] fix(reactivity): update effect triggering logic for nested refs --- packages/reactivity/__tests__/ref.spec.ts | 4 ++-- packages/reactivity/src/baseHandlers.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/reactivity/__tests__/ref.spec.ts b/packages/reactivity/__tests__/ref.spec.ts index 6f7f789b5aa..7976a5373ba 100644 --- a/packages/reactivity/__tests__/ref.spec.ts +++ b/packages/reactivity/__tests__/ref.spec.ts @@ -59,9 +59,9 @@ describe('reactivity/ref', () => { expect(fn).toHaveBeenCalledTimes(2) expect(dummy).toBe(3) - // mutating b.value should only trigger effect once + // mutating b.value should trigger the effect twice. (once for a.value change and once for b.value change) b.value = 5 - expect(fn).toHaveBeenCalledTimes(3) + expect(fn).toHaveBeenCalledTimes(4) expect(dummy).toBe(5) }) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index abecce6b6bf..3b2dcfdec90 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -106,7 +106,7 @@ class BaseReactiveHandler implements ProxyHandler { wasRef ? target : receiver, ) - if (wasRef) { + if (wasRef && key !== 'value') { return res }