From cc326a72d2db2a4e5a26e960903906865905381f Mon Sep 17 00:00:00 2001 From: teleskop150750 Date: Tue, 6 May 2025 13:30:32 +0300 Subject: [PATCH] feat: ref peek --- .../reactivity/__tests__/computed.spec.ts | 50 +++++++++++++++++++ packages/reactivity/__tests__/ref.spec.ts | 22 ++++++++ packages/reactivity/src/computed.ts | 8 +++ packages/reactivity/src/ref.ts | 16 +++++- 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index c7963499f..e70565f06 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -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) + }) + }) }) diff --git a/packages/reactivity/__tests__/ref.spec.ts b/packages/reactivity/__tests__/ref.spec.ts index 7976a5373..e2530d9b5 100644 --- a/packages/reactivity/__tests__/ref.spec.ts +++ b/packages/reactivity/__tests__/ref.spec.ts @@ -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) + }) + }) }) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index ea798e201..eec4545f9 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -26,9 +26,11 @@ interface BaseComputedRef extends Ref { export interface ComputedRef extends BaseComputedRef { readonly value: T + peek: () => T } export interface WritableComputedRef extends BaseComputedRef { + peek: () => T [WritableComputedRefSymbol]: true } @@ -151,6 +153,12 @@ export class ComputedRefImpl implements Subscriber { warn('Write operation failed: computed value is readonly') } } + + peek(): T { + refreshComputed(this) + + return this._value + } } /** diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 59b713dd8..738ad8f5c 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -34,6 +34,10 @@ export interface Ref { [RefSymbol]: true } +export interface RefWithPeek extends Ref { + 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( value: T, -): [T] extends [Ref] ? IfAny, T> : Ref, UnwrapRef | T> +): [T] extends [Ref] + ? IfAny, T> + : RefWithPeek, UnwrapRef | T> export function ref(): Ref export function ref(value?: unknown) { return createRef(value, false) @@ -66,6 +72,10 @@ export type ShallowRef = Ref & { [ShallowRefMarker]?: true } +export type ShallowRefWithPeek = ShallowRef & { + peek: () => T +} + /** * Shallow version of {@link ref}. * @@ -156,6 +166,10 @@ class RefImpl { } } } + + peek() { + return this._rawValue + } } /**