Skip to content

Commit aa06b10

Browse files
authored
feat(reactivity): proxyRefs method and ShallowUnwrapRefs type (#1682)
* feat(reactivity): `proxyRefs` method and `ShallowUnwrapRefs` type BREAKING CHANGE: template auto ref unwrapping are now applied shallowly, i.e. only at the root level. See #1682 for more details.
1 parent de62cc0 commit aa06b10

File tree

6 files changed

+39
-10
lines changed

6 files changed

+39
-10
lines changed

packages/reactivity/src/index.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
export {
22
ref,
3-
unref,
43
shallowRef,
54
isRef,
65
toRef,
76
toRefs,
7+
unref,
8+
proxyRefs,
89
customRef,
910
triggerRef,
1011
Ref,
11-
UnwrapRef,
1212
ToRefs,
13+
UnwrapRef,
14+
ShallowUnwrapRef,
1315
RefUnwrapBailTypes
1416
} from './ref'
1517
export {

packages/reactivity/src/ref.ts

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { track, trigger } from './effect'
22
import { TrackOpTypes, TriggerOpTypes } from './operations'
33
import { isObject, hasChanged } from '@vue/shared'
4-
import { reactive, isProxy, toRaw } from './reactive'
4+
import { reactive, isProxy, toRaw, isReactive } from './reactive'
55
import { CollectionTypes } from './collectionHandlers'
66

77
declare const RefSymbol: unique symbol
@@ -71,6 +71,27 @@ export function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
7171
return isRef(ref) ? (ref.value as any) : ref
7272
}
7373

74+
const shallowUnwrapHandlers: ProxyHandler<any> = {
75+
get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),
76+
set: (target, key, value, receiver) => {
77+
const oldValue = target[key]
78+
if (isRef(oldValue) && !isRef(value)) {
79+
oldValue.value = value
80+
return true
81+
} else {
82+
return Reflect.set(target, key, value, receiver)
83+
}
84+
}
85+
}
86+
87+
export function proxyRefs<T extends object>(
88+
objectWithRefs: T
89+
): ShallowUnwrapRef<T> {
90+
return isReactive(objectWithRefs)
91+
? objectWithRefs
92+
: new Proxy(objectWithRefs, shallowUnwrapHandlers)
93+
}
94+
7495
export type CustomRefFactory<T> = (
7596
track: () => void,
7697
trigger: () => void
@@ -146,6 +167,10 @@ type BaseTypes = string | number | boolean
146167
*/
147168
export interface RefUnwrapBailTypes {}
148169

170+
export type ShallowUnwrapRef<T> = {
171+
[K in keyof T]: T[K] extends Ref<infer V> ? V : T[K]
172+
}
173+
149174
export type UnwrapRef<T> = T extends Ref<infer V>
150175
? UnwrapRefSimple<V>
151176
: UnwrapRefSimple<T>

packages/runtime-core/src/component.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { VNode, VNodeChild, isVNode } from './vnode'
22
import {
3-
reactive,
43
ReactiveEffect,
54
pauseTracking,
65
resetTracking,
7-
shallowReadonly
6+
shallowReadonly,
7+
proxyRefs
88
} from '@vue/reactivity'
99
import {
1010
CreateComponentPublicInstance,
@@ -548,7 +548,7 @@ export function handleSetupResult(
548548
}
549549
// setup returned bindings.
550550
// assuming a render function compiled from template is present.
551-
instance.setupState = reactive(setupResult)
551+
instance.setupState = proxyRefs(setupResult)
552552
if (__DEV__) {
553553
exposeSetupStateOnRenderContext(instance)
554554
}

packages/runtime-core/src/componentProxy.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import {
1010
} from '@vue/shared'
1111
import {
1212
ReactiveEffect,
13-
UnwrapRef,
1413
toRaw,
1514
shallowReadonly,
1615
ReactiveFlags,
1716
track,
18-
TrackOpTypes
17+
TrackOpTypes,
18+
ShallowUnwrapRef
1919
} from '@vue/reactivity'
2020
import {
2121
ExtractComputedReturns,
@@ -154,7 +154,7 @@ export type ComponentPublicInstance<
154154
$nextTick: typeof nextTick
155155
$watch: typeof instanceWatch
156156
} & P &
157-
UnwrapRef<B> &
157+
ShallowUnwrapRef<B> &
158158
D &
159159
ExtractComputedReturns<C> &
160160
M &

packages/runtime-core/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export {
88
readonly,
99
// utilities
1010
unref,
11+
proxyRefs,
1112
isRef,
1213
toRef,
1314
toRefs,
@@ -125,6 +126,7 @@ export {
125126
ComputedRef,
126127
WritableComputedRef,
127128
UnwrapRef,
129+
ShallowUnwrapRef,
128130
WritableComputedOptions,
129131
ToRefs,
130132
DeepReadonly

test-dts/defineComponent.test-d.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ describe('with object props', () => {
146146

147147
// assert setup context unwrapping
148148
expectType<number>(this.c)
149-
expectType<string>(this.d.e)
149+
expectType<string>(this.d.e.value)
150150
expectType<GT>(this.f.g)
151151

152152
// setup context properties should be mutable

0 commit comments

Comments
 (0)