feat: ref peek

This commit is contained in:
teleskop150750 2025-05-06 13:30:32 +03:00
parent 56be3dd4db
commit cc326a72d2
4 changed files with 95 additions and 1 deletions

View File

@ -1150,4 +1150,54 @@ describe('reactivity/computed', () => {
const t2 = performance.now()
expect(t2 - t1).toBeLessThan(process.env.CI ? 100 : 30)
})
describe('peek', () => {
it('should return updated value', () => {
const value = reactive<{ foo?: number }>({})
const cValue = computed(() => value.foo)
expect(cValue.peek()).toBe(undefined)
value.foo = 1
expect(cValue.peek()).toBe(1)
})
it('should compute lazily', () => {
const value = reactive<{ foo?: number }>({})
const getter = vi.fn(() => value.foo)
const cValue = computed(getter)
// lazy
expect(getter).not.toHaveBeenCalled()
expect(cValue.peek()).toBe(undefined)
expect(getter).toHaveBeenCalledTimes(1)
// should not compute again
cValue.peek()
expect(getter).toHaveBeenCalledTimes(1)
// should not compute until needed
value.foo = 1
expect(getter).toHaveBeenCalledTimes(1)
// now it should compute
expect(cValue.peek()).toBe(1)
expect(getter).toHaveBeenCalledTimes(2)
// should not compute again
cValue.peek()
expect(getter).toHaveBeenCalledTimes(2)
})
it('should not trigger effect', () => {
const value = reactive<{ foo?: number }>({})
const cValue = computed(() => value.foo)
let dummy
effect(() => {
dummy = cValue.peek()
})
expect(dummy).toBe(undefined)
value.foo = 1
expect(dummy).toBe(undefined)
})
})
})

View File

@ -534,4 +534,26 @@ describe('reactivity/ref', () => {
// @ts-expect-error internal field
expect(objectRefValue._value).toBe(1)
})
describe('peek', () => {
it('should hold a value', () => {
const a = ref(1)
expect(a.peek()).toBe(1)
a.value = 2
expect(a.peek()).toBe(2)
})
it('should not be reactive', () => {
const a = ref(1)
let dummy
const fn = vi.fn(() => {
dummy = a.peek()
})
effect(fn)
expect(fn).toHaveBeenCalledTimes(1)
expect(dummy).toBe(1)
a.value = 2
expect(fn).toHaveBeenCalledTimes(1)
})
})
})

View File

@ -26,9 +26,11 @@ interface BaseComputedRef<T, S = T> extends Ref<T, S> {
export interface ComputedRef<T = any> extends BaseComputedRef<T> {
readonly value: T
peek: () => T
}
export interface WritableComputedRef<T, S = T> extends BaseComputedRef<T, S> {
peek: () => T
[WritableComputedRefSymbol]: true
}
@ -151,6 +153,12 @@ export class ComputedRefImpl<T = any> implements Subscriber {
warn('Write operation failed: computed value is readonly')
}
}
peek(): T {
refreshComputed(this)
return this._value
}
}
/**

View File

@ -34,6 +34,10 @@ export interface Ref<T = any, S = T> {
[RefSymbol]: true
}
export interface RefWithPeek<T = any, S = T> extends Ref<T, S> {
peek: () => T
}
/**
* Checks if a value is a ref object.
*
@ -54,7 +58,9 @@ export function isRef(r: any): r is Ref {
*/
export function ref<T>(
value: T,
): [T] extends [Ref] ? IfAny<T, Ref<T>, T> : Ref<UnwrapRef<T>, UnwrapRef<T> | T>
): [T] extends [Ref]
? IfAny<T, Ref<T>, T>
: RefWithPeek<UnwrapRef<T>, UnwrapRef<T> | T>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
return createRef(value, false)
@ -66,6 +72,10 @@ export type ShallowRef<T = any, S = T> = Ref<T, S> & {
[ShallowRefMarker]?: true
}
export type ShallowRefWithPeek<T = any, S = T> = ShallowRef<T, S> & {
peek: () => T
}
/**
* Shallow version of {@link ref}.
*
@ -156,6 +166,10 @@ class RefImpl<T = any> {
}
}
}
peek() {
return this._rawValue
}
}
/**