Skip to content

Commit cc326a7

Browse files
feat: ref peek
1 parent 56be3dd commit cc326a7

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

packages/reactivity/__tests__/computed.spec.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,4 +1150,54 @@ describe('reactivity/computed', () => {
11501150
const t2 = performance.now()
11511151
expect(t2 - t1).toBeLessThan(process.env.CI ? 100 : 30)
11521152
})
1153+
1154+
describe('peek', () => {
1155+
it('should return updated value', () => {
1156+
const value = reactive<{ foo?: number }>({})
1157+
const cValue = computed(() => value.foo)
1158+
expect(cValue.peek()).toBe(undefined)
1159+
value.foo = 1
1160+
expect(cValue.peek()).toBe(1)
1161+
})
1162+
1163+
it('should compute lazily', () => {
1164+
const value = reactive<{ foo?: number }>({})
1165+
const getter = vi.fn(() => value.foo)
1166+
const cValue = computed(getter)
1167+
1168+
// lazy
1169+
expect(getter).not.toHaveBeenCalled()
1170+
1171+
expect(cValue.peek()).toBe(undefined)
1172+
expect(getter).toHaveBeenCalledTimes(1)
1173+
1174+
// should not compute again
1175+
cValue.peek()
1176+
expect(getter).toHaveBeenCalledTimes(1)
1177+
1178+
// should not compute until needed
1179+
value.foo = 1
1180+
expect(getter).toHaveBeenCalledTimes(1)
1181+
1182+
// now it should compute
1183+
expect(cValue.peek()).toBe(1)
1184+
expect(getter).toHaveBeenCalledTimes(2)
1185+
1186+
// should not compute again
1187+
cValue.peek()
1188+
expect(getter).toHaveBeenCalledTimes(2)
1189+
})
1190+
1191+
it('should not trigger effect', () => {
1192+
const value = reactive<{ foo?: number }>({})
1193+
const cValue = computed(() => value.foo)
1194+
let dummy
1195+
effect(() => {
1196+
dummy = cValue.peek()
1197+
})
1198+
expect(dummy).toBe(undefined)
1199+
value.foo = 1
1200+
expect(dummy).toBe(undefined)
1201+
})
1202+
})
11531203
})

packages/reactivity/__tests__/ref.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,4 +534,26 @@ describe('reactivity/ref', () => {
534534
// @ts-expect-error internal field
535535
expect(objectRefValue._value).toBe(1)
536536
})
537+
538+
describe('peek', () => {
539+
it('should hold a value', () => {
540+
const a = ref(1)
541+
expect(a.peek()).toBe(1)
542+
a.value = 2
543+
expect(a.peek()).toBe(2)
544+
})
545+
546+
it('should not be reactive', () => {
547+
const a = ref(1)
548+
let dummy
549+
const fn = vi.fn(() => {
550+
dummy = a.peek()
551+
})
552+
effect(fn)
553+
expect(fn).toHaveBeenCalledTimes(1)
554+
expect(dummy).toBe(1)
555+
a.value = 2
556+
expect(fn).toHaveBeenCalledTimes(1)
557+
})
558+
})
537559
})

packages/reactivity/src/computed.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ interface BaseComputedRef<T, S = T> extends Ref<T, S> {
2626

2727
export interface ComputedRef<T = any> extends BaseComputedRef<T> {
2828
readonly value: T
29+
peek: () => T
2930
}
3031

3132
export interface WritableComputedRef<T, S = T> extends BaseComputedRef<T, S> {
33+
peek: () => T
3234
[WritableComputedRefSymbol]: true
3335
}
3436

@@ -151,6 +153,12 @@ export class ComputedRefImpl<T = any> implements Subscriber {
151153
warn('Write operation failed: computed value is readonly')
152154
}
153155
}
156+
157+
peek(): T {
158+
refreshComputed(this)
159+
160+
return this._value
161+
}
154162
}
155163

156164
/**

packages/reactivity/src/ref.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export interface Ref<T = any, S = T> {
3434
[RefSymbol]: true
3535
}
3636

37+
export interface RefWithPeek<T = any, S = T> extends Ref<T, S> {
38+
peek: () => T
39+
}
40+
3741
/**
3842
* Checks if a value is a ref object.
3943
*
@@ -54,7 +58,9 @@ export function isRef(r: any): r is Ref {
5458
*/
5559
export function ref<T>(
5660
value: T,
57-
): [T] extends [Ref] ? IfAny<T, Ref<T>, T> : Ref<UnwrapRef<T>, UnwrapRef<T> | T>
61+
): [T] extends [Ref]
62+
? IfAny<T, Ref<T>, T>
63+
: RefWithPeek<UnwrapRef<T>, UnwrapRef<T> | T>
5864
export function ref<T = any>(): Ref<T | undefined>
5965
export function ref(value?: unknown) {
6066
return createRef(value, false)
@@ -66,6 +72,10 @@ export type ShallowRef<T = any, S = T> = Ref<T, S> & {
6672
[ShallowRefMarker]?: true
6773
}
6874

75+
export type ShallowRefWithPeek<T = any, S = T> = ShallowRef<T, S> & {
76+
peek: () => T
77+
}
78+
6979
/**
7080
* Shallow version of {@link ref}.
7181
*
@@ -156,6 +166,10 @@ class RefImpl<T = any> {
156166
}
157167
}
158168
}
169+
170+
peek() {
171+
return this._rawValue
172+
}
159173
}
160174

161175
/**

0 commit comments

Comments
 (0)