This commit is contained in:
Валентин Степанов 2025-05-06 13:44:43 +03:00 committed by GitHub
commit e5c516fe10
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 117 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

@ -27,6 +27,7 @@ import {
pauseTracking,
resetTracking,
startBatch,
untrack,
} from '../src/effect'
describe('reactivity/effect', () => {
@ -1182,6 +1183,17 @@ describe('reactivity/effect', () => {
expect(spy2).toHaveBeenCalledTimes(2)
})
it('should not track dependencies when using untrack', () => {
const value = ref(1)
let dummy
effect(() => {
dummy = untrack(() => value.value)
})
expect(dummy).toBe(1)
value.value = 2
expect(dummy).toBe(1)
})
describe('dep unsubscribe', () => {
function getSubCount(dep: Dep | undefined) {
let count = 0

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

@ -537,6 +537,15 @@ export function resetTracking(): void {
shouldTrack = last === undefined ? true : last
}
export function untrack<T>(fn: () => T): T {
try {
pauseTracking()
return fn()
} finally {
resetTracking()
}
}
/**
* Registers a cleanup function for the current active effect.
* The cleanup function is called right before the next effect run, or when the

View File

@ -55,6 +55,7 @@ export {
enableTracking,
pauseTracking,
resetTracking,
untrack,
onEffectCleanup,
ReactiveEffect,
EffectFlags,

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
}
}
/**