UNPKG

codedsaif-react-hooks

Version:

To make it easy for you to get started with GitLab, here's a list of recommended next steps.

170 lines (158 loc) 5.7 kB
import { useEffect, useRef, useState } from 'react'; /** * useInterval * Runs a callback at a specified interval with support for pausing, debugging, initial delay, and external control. * * @param {Function} callback - Function to run at each interval tick. * @param {number|null} delay - Interval duration in ms. Use `null` to pause. * @param {Object} [options={}] - Additional options. * @param {boolean} [options.debug=false] - Log lifecycle events to the console. * @param {boolean} [options.stop=false] - Stops interval immediately when set to true. * @param {boolean} [options.immediate=false] - Fires callback immediately on mount. * @param {number} [options.initialDelay] - Wait this long before starting interval loop. * @param {number} [options.maxTicks] - Stop after this many ticks. * * @returns {{ * count: number, * reset: () => void, * forceTick: () => void, * pause: () => void, * resume: () => void, * }} * * @example * // Logs "Tick" every second * useInterval(() => console.log('Tick'), 1000); * * @example * const { count, reset } = useInterval(() => { * console.log('Tick!'); * }, 1000); * * @example * // With pause/resume capability * const [delay, setDelay] = useState(1000); * <button onClick={() => setDelay(prev => prev ? null : 1000)}>Toggle</button> * useInterval(() => console.log('Toggling...'), delay); * * @example * function CountDown({ initialCount, downLimit, initialDelay = 1000, bgColor = 'transparent' }) { * const [count, setCount] = useState(initialCount); * const [delay, setDelay] = useState(null); * useInterval(() => { * const newValue = count - 1; * setCount(newValue) * if (newValue <= downLimit) setDelay(null); * }, delay); * return ( * <div style={{ display: 'flex', backgroundColor: bgColor }}>{count} * <button onClick={() => setDelay(initialDelay)}>Start</button> * <button onClick={() => setDelay(null)}>Stop</button> * </div> * ) * } * * @example * import React, { useState, useCallback } from 'react'; * import useInterval from './useInterval'; * const ShoppingList = () => { * // Wait 5 seconds before fetching new data * const POLL_DELAY = 5000; * const [items, setItems] = useState([]); * const fetchItems = useCallback(async () => { * const response = await fetch('/shopping-list/items'); * const json = await response.json(); * setItems(json); * }, []); * useEffect(() => { * // Fetch items from API on mount * fetchItems(); * }, []); * useInterval(() => { * fetchItems(); * }, POLL_DELAY); * return ( * <ul> * {items.map((item) => <li>{item.title}</li>)} * </ul> * ) * }; */ const useInterval = (callback, delay, options) => { const { stop = false, debug = false, immediate = false, initialDelay, maxTicks, } = options || {}; const savedCallback = useRef(); const intervalId = useRef(null); const initialTimeoutId = useRef(null); const [tickCount, setTickCount] = useState(0); const tick = () => { if (debug) console.log('[⏱️ useInterval] Tick', tickCount + 1); setTickCount((prev) => prev + 1); savedCallback.current?.(); }; const clear = () => { if (intervalId.current) { clearInterval(intervalId.current); intervalId.current = null; if (debug) console.log('[🧹 useInterval] Interval cleared'); } if (initialTimeoutId.current) { clearTimeout(initialTimeoutId.current); initialTimeoutId.current = null; if (debug) console.log('[🧼 useInterval] Initial delay timeout cleared'); } }; const start = () => { if (intervalId.current || delay == null || stop) return; const runInterval = () => { intervalId.current = setInterval(tick, delay); if (debug) console.log('[🚀 useInterval] Interval started:', intervalId.current); }; if (initialDelay != null) { if (debug) console.log('[⏳ useInterval] Waiting initialDelay:', initialDelay); initialTimeoutId.current = setTimeout(() => { runInterval(); }, initialDelay); } else { runInterval(); } }; useEffect(() => { savedCallback.current = callback; if (debug) console.log('[💾 useInterval] Callback updated'); }, [callback]); useEffect(() => { if (stop || delay === null || delay === undefined) { clear(); return; } start(); return clear; }, [delay, debug, stop, initialDelay]); useEffect(() => { if (maxTicks != null && tickCount >= maxTicks) { clear(); if (debug) console.log('[✅ useInterval] Max ticks reached'); } }, [tickCount]); useEffect(() => { if (immediate && typeof savedCallback.current === 'function') { savedCallback.current(); setTickCount((prev) => prev + 1); if (debug) console.log('[⚡ useInterval] Immediate tick fired'); } }, []); return { count: tickCount, reset: () => setTickCount(0), forceTick: () => tick(), pause: clear, resume: start, }; }; export default useInterval;