Skip to content

Commit e9024bf

Browse files
committed
feat(reactivity): expose unref and shallowRef
1 parent 0c67201 commit e9024bf

File tree

6 files changed

+59
-11
lines changed

6 files changed

+59
-11
lines changed

packages/reactivity/__tests__/ref.spec.ts

+30-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
import { ref, effect, reactive, isRef, toRefs, Ref } from '../src/index'
1+
import {
2+
ref,
3+
effect,
4+
reactive,
5+
isRef,
6+
toRefs,
7+
Ref,
8+
isReactive
9+
} from '../src/index'
210
import { computed } from '@vue/runtime-dom'
11+
import { shallowRef, unref } from '../src/ref'
312

413
describe('reactivity/ref', () => {
514
it('should hold a value', () => {
@@ -129,6 +138,26 @@ describe('reactivity/ref', () => {
129138
expect(tupleRef.value[4].value).toBe(1)
130139
})
131140

141+
test('unref', () => {
142+
expect(unref(1)).toBe(1)
143+
expect(unref(ref(1))).toBe(1)
144+
})
145+
146+
test('shallowRef', () => {
147+
const sref = shallowRef({ a: 1 })
148+
expect(isReactive(sref.value)).toBe(false)
149+
150+
let dummy
151+
effect(() => {
152+
dummy = sref.value.a
153+
})
154+
expect(dummy).toBe(1)
155+
156+
sref.value = { a: 2 }
157+
expect(isReactive(sref.value)).toBe(false)
158+
expect(dummy).toBe(2)
159+
})
160+
132161
test('isRef', () => {
133162
expect(isRef(ref(1))).toBe(true)
134163
expect(isRef(computed(() => 1))).toBe(true)

packages/reactivity/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { ref, isRef, toRefs, Ref, UnwrapRef } from './ref'
1+
export { ref, unref, shallowRef, isRef, toRefs, Ref, UnwrapRef } from './ref'
22
export {
33
reactive,
44
isReactive,

packages/reactivity/src/ref.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,30 @@ export function isRef(r: any): r is Ref {
3131
export function ref<T>(value: T): T extends Ref ? T : Ref<T>
3232
export function ref<T = any>(): Ref<T>
3333
export function ref(value?: unknown) {
34+
return createRef(value)
35+
}
36+
37+
export function shallowRef<T>(value: T): T extends Ref ? T : Ref<T>
38+
export function shallowRef<T = any>(): Ref<T>
39+
export function shallowRef(value?: unknown) {
40+
return createRef(value, true)
41+
}
42+
43+
function createRef(value: unknown, shallow = false) {
3444
if (isRef(value)) {
3545
return value
3646
}
37-
value = convert(value)
47+
if (!shallow) {
48+
value = convert(value)
49+
}
3850
const r = {
3951
_isRef: true,
4052
get value() {
4153
track(r, TrackOpTypes.GET, 'value')
4254
return value
4355
},
4456
set value(newVal) {
45-
value = convert(newVal)
57+
value = shallow ? newVal : convert(newVal)
4658
trigger(
4759
r,
4860
TriggerOpTypes.SET,
@@ -54,6 +66,10 @@ export function ref(value?: unknown) {
5466
return r
5567
}
5668

69+
export function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
70+
return isRef(ref) ? (ref.value as any) : ref
71+
}
72+
5773
export function toRefs<T extends object>(
5874
object: T
5975
): { [K in keyof T]: Ref<T[K]> } {

packages/runtime-core/src/componentProxy.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
isRef,
1414
isReactive,
1515
Ref,
16-
ComputedRef
16+
ComputedRef,
17+
unref
1718
} from '@vue/reactivity'
1819
import { warn } from './warning'
1920
import { Slots } from './componentSlots'
@@ -84,8 +85,6 @@ const enum AccessTypes {
8485
OTHER
8586
}
8687

87-
const unwrapRef = (val: unknown) => (isRef(val) ? val.value : val)
88-
8988
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
9089
get(target: ComponentInternalInstance, key: string) {
9190
// fast path for unscopables when using `with` block
@@ -115,7 +114,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
115114
case AccessTypes.DATA:
116115
return data[key]
117116
case AccessTypes.CONTEXT:
118-
return unwrapRef(renderContext[key])
117+
return unref(renderContext[key])
119118
case AccessTypes.PROPS:
120119
return propsProxy![key]
121120
// default: just fallthrough
@@ -125,7 +124,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
125124
return data[key]
126125
} else if (hasOwn(renderContext, key)) {
127126
accessCache![key] = AccessTypes.CONTEXT
128-
return unwrapRef(renderContext[key])
127+
return unref(renderContext[key])
129128
} else if (type.props != null) {
130129
// only cache other properties when instance has declared (this stable)
131130
// props

packages/runtime-core/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
export const version = __VERSION__
44
export {
55
ref,
6+
unref,
7+
shallowRef,
68
isRef,
79
toRefs,
810
reactive,

test-dts/ref.test-d.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { expectType } from 'tsd'
2-
import { Ref, ref } from './index'
3-
import { isRef } from '@vue/reactivity'
2+
import { Ref, ref, isRef, unref } from './index'
43

54
function foo(arg: number | Ref<number>) {
65
// ref coercing
@@ -11,6 +10,9 @@ function foo(arg: number | Ref<number>) {
1110
if (isRef(arg)) {
1211
expectType<Ref<number>>(arg)
1312
}
13+
14+
// ref unwrapping
15+
expectType<number>(unref(arg))
1416
}
1517

1618
foo(1)

0 commit comments

Comments
 (0)