react-timing-hooks
Advanced tools
Comparing version 4.0.0 to 4.0.1
@@ -16,2 +16,3 @@ import useTimeoutEffect from './timeout/useTimeoutEffect'; | ||
import { DebounceOptions, useDebounce } from './timeout/useDebounce'; | ||
export { useAnimationFrame, useAnimationFrameLoop, useClock, useCountdown, useCounter, useDebounce, useIdleCallback, useIdleCallbackEffect, useInterval, useTimer, useTimeout, useTimeoutEffect, ClockOptions, CountdownOptions, CounterSettings, DebounceOptions, IdleCallbackEffectCallback, IntervalControls, IntervalOptions, TimeoutEffectCallback, }; | ||
import { ThrottleOptions, useThrottle } from './timeout/useThrottle'; | ||
export { useAnimationFrame, useAnimationFrameLoop, useClock, useCountdown, useCounter, useDebounce, useIdleCallback, useIdleCallbackEffect, useInterval, useThrottle, useTimer, useTimeout, useTimeoutEffect, ClockOptions, CountdownOptions, CounterSettings, DebounceOptions, IdleCallbackEffectCallback, IntervalControls, IntervalOptions, ThrottleOptions, TimeoutEffectCallback, }; |
@@ -423,2 +423,68 @@ import { useRef, useCallback, useEffect, useState } from 'react'; | ||
export { useAnimationFrame, useAnimationFrameLoop, useClock, useCountdown, useCounter, useDebounce, useIdleCallback, useIdleCallbackEffect, useInterval, useTimeout, useTimeoutEffect, useTimer }; | ||
/** | ||
* Throttles a callback. | ||
* | ||
* Can be used for **rate-limiting** β the callback will only be invoked every X milliseconds (X being the set timeout), | ||
* even if it was called more frequently. | ||
* | ||
* Similar, but different(!), is the `useDebounce()` hook, which blocks the invocation entirely until the function was | ||
* stopped being called for X milliseconds. | ||
* | ||
* By default, the throttled function will always be called immediately (`options.leading` is true by default) and then | ||
* (`options.trailing` is true by default) also after every X milliseconds for consecutive calls. | ||
* | ||
* @param callback | ||
* @param waitMs Minimum waiting time between consecutive calls | ||
* @param options | ||
* @param [options.leading = true] If true, invoke the callback immediately/before the timeout | ||
* @param [options.trailing = true] If true, queue invocations for after the timeout | ||
*/ | ||
function useThrottle(callback, waitMs, options = {}) { | ||
const { leading = true, trailing = true } = options; | ||
const hadUnsuccessfulAttempt = useRef(false); | ||
const trailingArgs = useRef(null); | ||
const throttledCallback = useRef(callback); | ||
const timeoutId = useRef(null); | ||
useEffect(() => { | ||
throttledCallback.current = callback; | ||
}, [callback]); | ||
useEffect(() => { | ||
return () => { | ||
if (timeoutId.current) { | ||
clearTimeout(timeoutId.current); | ||
} | ||
}; | ||
}, []); | ||
const execThrottled = useCallback((...args) => { | ||
if (timeoutId.current) { | ||
if (trailing) { | ||
hadUnsuccessfulAttempt.current = true; | ||
trailingArgs.current = args; | ||
} | ||
return timeoutId.current; | ||
} | ||
if (!hadUnsuccessfulAttempt.current) { | ||
if (leading) { | ||
throttledCallback.current(...args); | ||
} | ||
else if (trailing) { | ||
hadUnsuccessfulAttempt.current = true; | ||
trailingArgs.current = args; | ||
} | ||
} | ||
timeoutId.current = setTimeout(() => { | ||
timeoutId.current = null; | ||
if (hadUnsuccessfulAttempt.current) { | ||
if (trailing) { | ||
throttledCallback.current(...trailingArgs.current); | ||
} | ||
execThrottled(...trailingArgs.current); | ||
hadUnsuccessfulAttempt.current = false; | ||
} | ||
}, waitMs); | ||
return timeoutId.current; | ||
}, [waitMs]); | ||
return execThrottled; | ||
} | ||
export { useAnimationFrame, useAnimationFrameLoop, useClock, useCountdown, useCounter, useDebounce, useIdleCallback, useIdleCallbackEffect, useInterval, useThrottle, useTimeout, useTimeoutEffect, useTimer }; |
@@ -427,2 +427,68 @@ 'use strict'; | ||
/** | ||
* Throttles a callback. | ||
* | ||
* Can be used for **rate-limiting** β the callback will only be invoked every X milliseconds (X being the set timeout), | ||
* even if it was called more frequently. | ||
* | ||
* Similar, but different(!), is the `useDebounce()` hook, which blocks the invocation entirely until the function was | ||
* stopped being called for X milliseconds. | ||
* | ||
* By default, the throttled function will always be called immediately (`options.leading` is true by default) and then | ||
* (`options.trailing` is true by default) also after every X milliseconds for consecutive calls. | ||
* | ||
* @param callback | ||
* @param waitMs Minimum waiting time between consecutive calls | ||
* @param options | ||
* @param [options.leading = true] If true, invoke the callback immediately/before the timeout | ||
* @param [options.trailing = true] If true, queue invocations for after the timeout | ||
*/ | ||
function useThrottle(callback, waitMs, options = {}) { | ||
const { leading = true, trailing = true } = options; | ||
const hadUnsuccessfulAttempt = react.useRef(false); | ||
const trailingArgs = react.useRef(null); | ||
const throttledCallback = react.useRef(callback); | ||
const timeoutId = react.useRef(null); | ||
react.useEffect(() => { | ||
throttledCallback.current = callback; | ||
}, [callback]); | ||
react.useEffect(() => { | ||
return () => { | ||
if (timeoutId.current) { | ||
clearTimeout(timeoutId.current); | ||
} | ||
}; | ||
}, []); | ||
const execThrottled = react.useCallback((...args) => { | ||
if (timeoutId.current) { | ||
if (trailing) { | ||
hadUnsuccessfulAttempt.current = true; | ||
trailingArgs.current = args; | ||
} | ||
return timeoutId.current; | ||
} | ||
if (!hadUnsuccessfulAttempt.current) { | ||
if (leading) { | ||
throttledCallback.current(...args); | ||
} | ||
else if (trailing) { | ||
hadUnsuccessfulAttempt.current = true; | ||
trailingArgs.current = args; | ||
} | ||
} | ||
timeoutId.current = setTimeout(() => { | ||
timeoutId.current = null; | ||
if (hadUnsuccessfulAttempt.current) { | ||
if (trailing) { | ||
throttledCallback.current(...trailingArgs.current); | ||
} | ||
execThrottled(...trailingArgs.current); | ||
hadUnsuccessfulAttempt.current = false; | ||
} | ||
}, waitMs); | ||
return timeoutId.current; | ||
}, [waitMs]); | ||
return execThrottled; | ||
} | ||
exports.useAnimationFrame = useAnimationFrame; | ||
@@ -437,4 +503,5 @@ exports.useAnimationFrameLoop = useAnimationFrameLoop; | ||
exports.useInterval = useInterval; | ||
exports.useThrottle = useThrottle; | ||
exports.useTimeout = useTimeout; | ||
exports.useTimeoutEffect = useTimeoutEffect; | ||
exports.useTimer = useTimer; |
{ | ||
"name": "react-timing-hooks", | ||
"version": "4.0.0", | ||
"version": "4.0.1", | ||
"description": "React hooks for setTimeout, setInterval, requestAnimationFrame, requestIdleCallback", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -50,3 +50,3 @@ <img alt="logo" src="https://github.com/EricLambrecht/react-timing-hooks/raw/main/logo.png" width="680" /> | ||
#### Migrate from v3 to v4 | ||
**To migrate from v3 to v4:** | ||
@@ -53,0 +53,0 @@ https://ericlambrecht.github.io/react-timing-hooks/migrations/ |
68360
1204