Skip to content

refactor(watch): replace WatchStopHandle with WatchHandle in watchAtMost, watchDebounced, watchIgnorable, watchTriggerable, and watchWithFilter for consistency and improved functionality #4852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Prev Previous commit
Next Next commit
refactor: replace WatchStopHandle with WatchHandle in watchAtMost, wa…
…tchDebounced, watchIgnorable, watchTriggerable, and watchWithFilter for consistency and improved functionality
  • Loading branch information
ArthurDarkstone committed Jul 4, 2025
commit cc2b011b9f8db766910ec1a2d651f53b08aff72d
15 changes: 9 additions & 6 deletions packages/shared/watchAtMost/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { MaybeRefOrGetter, ShallowRef, WatchCallback, WatchSource, WatchStopHandle } from 'vue'
import type { MaybeRefOrGetter, ShallowRef, WatchCallback, WatchHandle, WatchSource } from 'vue'
import type { MapOldSources, MapSources } from '../utils'
import type { WatchWithFilterOptions } from '../watchWithFilter'
import { nextTick, shallowRef, toValue } from 'vue'
Expand All @@ -8,8 +8,7 @@ export interface WatchAtMostOptions<Immediate> extends WatchWithFilterOptions<Im
count: MaybeRefOrGetter<number>
}

export interface WatchAtMostReturn {
stop: WatchStopHandle
export interface WatchAtMostReturn extends WatchHandle {
count: ShallowRef<number>
}

Expand All @@ -31,17 +30,21 @@ export function watchAtMost<Immediate extends Readonly<boolean> = false>(

const current = shallowRef(0)

const stop = watchWithFilter(
const watchHandle = watchWithFilter(
source,
(...args) => {
current.value += 1
if (current.value >= toValue(count))
nextTick(() => stop())
nextTick(() => watchHandle.stop())

cb(...args)
},
watchOptions,
)

return { count: current, stop }
const res = watchHandle as WatchAtMostReturn

res.count = current

return res
}
4 changes: 2 additions & 2 deletions packages/shared/watchDebounced/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { MaybeRefOrGetter, WatchCallback, WatchOptions, WatchSource, WatchStopHandle } from 'vue'
import type { MaybeRefOrGetter, WatchCallback, WatchHandle, WatchOptions, WatchSource, WatchStopHandle } from 'vue'
import type { DebounceFilterOptions, MapOldSources, MapSources } from '../utils'
import { debounceFilter } from '../utils'
import { watchWithFilter } from '../watchWithFilter'
Expand All @@ -17,7 +17,7 @@ export function watchDebounced<Immediate extends Readonly<boolean> = false>(
source: any,
cb: any,
options: WatchDebouncedOptions<Immediate> = {},
): WatchStopHandle {
): WatchHandle {
const {
debounce = 0,
maxWait = undefined,
Expand Down
8 changes: 7 additions & 1 deletion packages/shared/watchIgnorable/demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { shallowRef } from 'vue'
const log = shallowRef('')
const source = shallowRef(0)

const { ignoreUpdates } = watchIgnorable(
const { ignoreUpdates, pause, resume } = watchIgnorable(
source,
v => (log.value += `Changed to "${v}"\n`),
{ flush: 'sync' },
Expand Down Expand Up @@ -33,6 +33,12 @@ function ignoredUpdate() {
<button class="orange" @click="ignoredUpdate">
Ignored Update
</button>
<button @click="pause">
Pause
</button>
<button @click="resume">
Resume
</button>
<button @click="clear">
Reset
</button>
Expand Down
34 changes: 25 additions & 9 deletions packages/shared/watchIgnorable/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { WatchCallback, WatchSource, WatchStopHandle } from 'vue'
import type { Fn, MapOldSources, MapSources } from '../utils'
import type { WatchCallback, WatchHandle, WatchSource } from 'vue'
import type { MapOldSources, MapSources } from '../utils'
import type { WatchWithFilterOptions } from '../watchWithFilter'
import { watch } from 'vue'
import { bypassFilter, createFilterWrapper } from '../utils'
Expand All @@ -11,10 +11,9 @@ import { bypassFilter, createFilterWrapper } from '../utils'
export type IgnoredUpdater = (updater: () => void) => void
export type IgnoredPrevAsyncUpdates = () => void

export interface WatchIgnorableReturn {
export interface WatchIgnorableReturn extends WatchHandle {
ignoreUpdates: IgnoredUpdater
ignorePrevAsyncUpdates: IgnoredPrevAsyncUpdates
stop: WatchStopHandle
}

export function watchIgnorable<T extends Readonly<WatchSource<unknown>[]>, Immediate extends Readonly<boolean> = false>(sources: [...T], cb: WatchCallback<MapSources<T>, MapOldSources<T, Immediate>>, options?: WatchWithFilterOptions<Immediate>): WatchIgnorableReturn
Expand All @@ -38,7 +37,7 @@ export function watchIgnorable<Immediate extends Readonly<boolean> = false>(

let ignoreUpdates: IgnoredUpdater
let ignorePrevAsyncUpdates: IgnoredPrevAsyncUpdates
let stop: WatchStopHandle
let watchHandle: WatchHandle

if (watchOptions.flush === 'sync') {
let ignore = false
Expand All @@ -54,7 +53,7 @@ export function watchIgnorable<Immediate extends Readonly<boolean> = false>(
ignore = false
}

stop = watch(
watchHandle = watch(
source,
(...args) => {
if (!ignore)
Expand All @@ -66,7 +65,7 @@ export function watchIgnorable<Immediate extends Readonly<boolean> = false>(
else {
// flush 'pre' and 'post'

const disposables: Fn[] = []
const disposables: WatchHandle[] = []

// counters for how many following changes to be ignored
// ignoreCounter is incremented before there is a history operation
Expand Down Expand Up @@ -121,12 +120,29 @@ export function watchIgnorable<Immediate extends Readonly<boolean> = false>(
),
)

stop = () => {
watchHandle = (() => {
disposables.forEach(fn => fn())
}) as WatchHandle

watchHandle.stop = () => {
disposables.forEach(fn => fn())
}

watchHandle.pause = () => {
disposables.forEach(fn => fn.pause())
}

watchHandle.resume = () => {
disposables.forEach(fn => fn.resume())
}
}

return { stop, ignoreUpdates, ignorePrevAsyncUpdates }
const res = watchHandle as WatchIgnorableReturn

res.ignoreUpdates = ignoreUpdates
res.ignorePrevAsyncUpdates = ignorePrevAsyncUpdates

return res
}

// alias
Expand Down
8 changes: 7 additions & 1 deletion packages/shared/watchTriggerable/demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { shallowRef } from 'vue'
const log = shallowRef('')
const source = shallowRef(0)

const { trigger, ignoreUpdates } = watchTriggerable(
const { trigger, ignoreUpdates, pause, resume } = watchTriggerable(
source,
async (v, _, onCleanup) => {
let canceled = false
Expand Down Expand Up @@ -37,6 +37,12 @@ function update() {
<button class="orange" @click="trigger">
Manual Trigger
</button>
<button @click="pause">
Pause
</button>
<button @click="resume">
Resume
</button>
<button @click="clear">
Reset
</button>
Expand Down
13 changes: 7 additions & 6 deletions packages/shared/watchTriggerable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export function watchTriggerable<Immediate extends Readonly<boolean> = false>(

return cb(value, oldValue, onCleanup)
}
const res = watchIgnorable(source, _cb, options)
const { ignoreUpdates } = res
const watchHandle = watchIgnorable(source, _cb, options)
const { ignoreUpdates } = watchHandle

const trigger = () => {
let res: any
Expand All @@ -62,10 +62,11 @@ export function watchTriggerable<Immediate extends Readonly<boolean> = false>(
return res
}

return {
...res,
trigger,
}
const res = watchHandle as WatchTriggerableReturn

res.trigger = trigger

return res
}

function getWatchSources(sources: any) {
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/watchWithFilter/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { WatchCallback, WatchOptions, WatchSource, WatchStopHandle } from 'vue'
import type { WatchCallback, WatchHandle, WatchOptions, WatchSource } from 'vue'
import type { ConfigurableEventFilter, MapOldSources, MapSources } from '../utils'
import { watch } from 'vue'
import { bypassFilter, createFilterWrapper } from '../utils'
Expand All @@ -6,16 +6,16 @@
export interface WatchWithFilterOptions<Immediate> extends WatchOptions<Immediate>, ConfigurableEventFilter {}

// overloads
export function watchWithFilter<T extends Readonly<WatchSource<unknown>[]>, Immediate extends Readonly<boolean> = false>(sources: [...T], cb: WatchCallback<MapSources<T>, MapOldSources<T, Immediate>>, options?: WatchWithFilterOptions<Immediate>): WatchStopHandle

Check failure on line 9 in packages/shared/watchWithFilter/index.ts

View workflow job for this annotation

GitHub Actions / test (20.x)

Cannot find name 'WatchStopHandle'. Did you mean 'WatchHandle'?

Check failure on line 9 in packages/shared/watchWithFilter/index.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Cannot find name 'WatchStopHandle'. Did you mean 'WatchHandle'?
export function watchWithFilter<T, Immediate extends Readonly<boolean> = false>(source: WatchSource<T>, cb: WatchCallback<T, Immediate extends true ? T | undefined : T>, options?: WatchWithFilterOptions<Immediate>): WatchStopHandle

Check failure on line 10 in packages/shared/watchWithFilter/index.ts

View workflow job for this annotation

GitHub Actions / test (20.x)

Cannot find name 'WatchStopHandle'. Did you mean 'WatchHandle'?

Check failure on line 10 in packages/shared/watchWithFilter/index.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Cannot find name 'WatchStopHandle'. Did you mean 'WatchHandle'?
export function watchWithFilter<T extends object, Immediate extends Readonly<boolean> = false>(source: T, cb: WatchCallback<T, Immediate extends true ? T | undefined : T>, options?: WatchWithFilterOptions<Immediate>): WatchStopHandle

Check failure on line 11 in packages/shared/watchWithFilter/index.ts

View workflow job for this annotation

GitHub Actions / test (20.x)

Cannot find name 'WatchStopHandle'. Did you mean 'WatchHandle'?

Check failure on line 11 in packages/shared/watchWithFilter/index.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Cannot find name 'WatchStopHandle'. Did you mean 'WatchHandle'?

// implementation
export function watchWithFilter<Immediate extends Readonly<boolean> = false>(
source: any,
cb: any,
options: WatchWithFilterOptions<Immediate> = {},
): WatchStopHandle {
): WatchHandle {
const {
eventFilter = bypassFilter,
...watchOptions
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy