useDebounceFn
Menunda sebuah fungsi sampai pemanggilannya berhenti sejenak.
Ditekan: 0
Benar-benar jalan: 0
Ditulis sendiri, bukan membungkus paket debounce mana pun. Itu keputusan yang disengaja: composable Kliping tidak boleh membawa dependensi runtime. Kalau memasang @kliping/use-debounce-fn memaksa orang menambah satu paket, mereka akan memakai paketnya langsung dan registry ini kehilangan alasan keberadaannya.
Yang didapat sebagai gantinya bukan cuma nol dependensi: timernya ikut mati bersama scope-nya. Debounce dari paket mana pun tidak tahu apa-apa soal siklus hidup Vue, jadi pemanggilnya harus ingat membatalkannya sendiri — dan itu jenis hal yang selalu terlupa.
wait boleh reaktif. Yang dibaca adalah nilainya saat run() dipanggil, bukan saat composable-nya dibuat.
Pemasangan
npx shadcn-vue@latest add @kliping/use-debounce-fn Perlu @kliping terdaftar di components.json project Anda. Lihat cara menyiapkannya.
Tanpa dependensi — tidak ada berkas lain yang ikut terpasang.
Salin kode berikut ke project Anda.
import { getCurrentScope, onScopeDispose, toValue } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
export interface UseDebounceFnOptions {
/** Jalankan sekali di awal, sebelum jedanya dihitung. Bawaan: false. */
leading?: boolean
/** Jalankan di akhir jeda. Bawaan: true. */
trailing?: boolean
}
export function useDebounceFn<Fn extends (...args: any[]) => any>(
fn: Fn,
wait: MaybeRefOrGetter<number> = 1000,
options: UseDebounceFnOptions = {},
) {
const { leading = false, trailing = true } = options
let timer: ReturnType<typeof setTimeout> | undefined
let argumenTerakhir: Parameters<Fn> | undefined
const hentikanTimer = () => {
if (timer !== undefined) {
clearTimeout(timer)
timer = undefined
}
}
/** Buang panggilan yang sedang menunggu. Yang sudah terlanjur jalan tidak ditarik. */
const cancel = () => {
hentikanTimer()
argumenTerakhir = undefined
}
/** Jalankan sekarang juga panggilan yang sedang menunggu, tanpa menunggu jedanya habis. */
const flush = () => {
if (timer === undefined || !argumenTerakhir)
return
const argumen = argumenTerakhir
cancel()
fn(...argumen)
}
const run = (...args: Parameters<Fn>) => {
// Dibaca sebelum timernya dihentikan: yang menentukan panggilan ini "awal"
// adalah tidak adanya timer yang sedang berjalan, bukan keadaan sesudahnya.
const awal = timer === undefined
hentikanTimer()
argumenTerakhir = args
if (leading && awal) {
argumenTerakhir = undefined
fn(...args)
}
timer = setTimeout(() => {
timer = undefined
if (trailing && argumenTerakhir) {
const argumen = argumenTerakhir
argumenTerakhir = undefined
fn(...argumen)
}
}, toValue(wait))
}
if (getCurrentScope())
onScopeDispose(cancel)
return { run, cancel, flush }
}API
export interface UseDebounceFnOptions {
/** Jalankan sekali di awal, sebelum jedanya dihitung. Bawaan: false. */
leading?: boolean
/** Jalankan di akhir jeda. Bawaan: true. */
trailing?: boolean
}
export function useDebounceFn<Fn extends (...args: any[]) => any>(
fn: Fn,
wait: MaybeRefOrGetter<number> = 1000,
options: UseDebounceFnOptions = {},
)