Bedanya dari setInterval telanjang ada tiga, dan ketiganya sering jadi sumber cacat: timernya ikut mati bersama scope, jedanya bisa diganti tanpa memasang ulang, dan null menghentikannya tanpa perlu cabang if di pemanggilnya.
delay berupa null atau angka negatif berarti berhenti. Itu yang membuat pola "jalan hanya saat tab terlihat" jadi satu baris:
ts
const terlihat = useDocumentVisibility()
useInterval(muatUlang, () => (terlihat.value === 'visible' ? 5000 : null))Installation
pnpm dlx shadcn-vue@latest add @kliping/use-interval Perlu @kliping terdaftar di components.json project Anda. Lihat cara menyiapkannya.
Ikut terpasang sendiri: is-browser.
Salin dulu is-browser — masing-masing memuat sumbernya di halamannya sendiri — lalu tempel berkas ini.
ts
import { getCurrentScope, onScopeDispose, readonly, ref, toValue, watch } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
import { isBrowser } from '@/lib/is-browser'
export interface UseIntervalOptions {
/** Run once immediately, before the first delay elapses. */
immediate?: boolean
}
export function useInterval(
fn: () => void,
delay: MaybeRefOrGetter<number | null> = 1000,
options: UseIntervalOptions = {},
) {
const { immediate = false } = options
const isActive = ref(false)
let timer: ReturnType<typeof setInterval> | undefined
const pause = () => {
if (timer !== undefined) {
clearInterval(timer)
timer = undefined
}
isActive.value = false
}
const start = (interval: number) => {
pause()
if (!isBrowser) return
if (immediate) fn()
timer = setInterval(fn, interval)
isActive.value = true
}
const resume = () => {
const interval = toValue(delay)
if (typeof interval === 'number' && interval >= 0) start(interval)
}
watch(
() => toValue(delay),
(interval) => {
if (typeof interval !== 'number' || interval < 0) {
pause()
return
}
start(interval)
},
{ immediate: true },
)
if (getCurrentScope()) onScopeDispose(pause)
return { isActive: readonly(isActive), pause, resume }
}API
ts
export interface UseIntervalOptions {
/** Run once immediately, before the first delay elapses. */
immediate?: boolean
}
export function useInterval(
fn: () => void,
delay: MaybeRefOrGetter<number | null> = 1000,
options: UseIntervalOptions = {},
)