react-timing-hooks
Advanced tools
Comparing version 3.2.2 to 4.0.0
@@ -8,3 +8,3 @@ /** | ||
*/ | ||
declare const useAnimationFrame: <T extends (...args: never[]) => unknown>(callback: T) => (...args: Parameters<T>) => void; | ||
declare const useAnimationFrame: <T extends (...args: never[]) => unknown>(callback: T) => (...args: Parameters<T>) => number; | ||
export default useAnimationFrame; |
@@ -1,2 +0,7 @@ | ||
declare const useAnimationFrameLoop: <T extends (...args: never[]) => unknown>(callback: T, pause?: boolean) => void; | ||
import { Controls } from '../controls/useControls'; | ||
type AnimationFrameLoopControls = Omit<Controls, 'isPausedRef'>; | ||
type AnimationFrameLoopOptions = { | ||
startOnMount?: boolean; | ||
}; | ||
declare const useAnimationFrameLoop: <T extends (...args: never[]) => unknown>(callback: T, options?: AnimationFrameLoopOptions) => AnimationFrameLoopControls; | ||
export default useAnimationFrameLoop; |
import useTimeoutEffect from './timeout/useTimeoutEffect'; | ||
import { TimeoutEffectCallback } from './timeout/types'; | ||
import useTimeout from './timeout/useTimeout'; | ||
import useInterval, { IntervalControls } from './interval/useInterval'; | ||
import useInterval, { IntervalControls, IntervalOptions } from './interval/useInterval'; | ||
import useTimer from './interval/useTimer'; | ||
@@ -13,4 +13,5 @@ import useIdleCallbackEffect from './idle-callback/useIdleCallbackEffect'; | ||
import { ClockOptions } from './interval/useClock'; | ||
import useCounter from './interval/useCounter'; | ||
import useCountdown from './interval/useCountdown'; | ||
export { useAnimationFrame, useAnimationFrameLoop, useClock, useIdleCallback, useIdleCallbackEffect, useInterval, useTimer, useTimeout, useTimeoutEffect, useCounter, useCountdown, IntervalControls, IdleCallbackEffectCallback, TimeoutEffectCallback, ClockOptions, }; | ||
import useCounter, { CounterSettings } from './interval/useCounter'; | ||
import useCountdown, { CountdownOptions } from './interval/useCountdown'; | ||
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, }; |
@@ -39,12 +39,16 @@ import { useRef, useCallback, useEffect, useState } from 'react'; | ||
* | ||
* Pending callbacks will be cleared in case the component unmounts. | ||
* This **will not debounce or throttle** the callbacks, i.e. consecutive calls of this function will all spawn | ||
* new timeouts even if some are still pending. If you want a debouncing version, take a look at `useDebounce()`. | ||
* If you want a throttling version, see `useThrottle()`. | ||
* | ||
* Pending callbacks will only(!) be cleared in case the component unmounts. | ||
* | ||
* @param callback The callback that is invoked after the timeout expired | ||
* @param timeout A timeout in milliseconds | ||
* @param waitMs The timeout in milliseconds | ||
* | ||
* @returns a function that executes the provided callback after the specified amount of time | ||
*/ | ||
function useTimeout(callback, timeout) { | ||
function useTimeout(callback, waitMs) { | ||
const timeoutCallback = useRef(callback); | ||
const [timeoutId, setTimeoutId] = useState(null); | ||
const timeoutIds = useRef([]); | ||
useEffect(() => { | ||
@@ -55,41 +59,54 @@ timeoutCallback.current = callback; | ||
return () => { | ||
if (timeoutId) { | ||
clearTimeout(timeoutId); | ||
} | ||
timeoutIds.current.forEach((id) => clearTimeout(id)); | ||
}; | ||
}, [timeoutId]); | ||
}, [timeoutIds]); | ||
return useCallback((...args) => { | ||
const id = setTimeout(() => timeoutCallback.current(...args), timeout); | ||
setTimeoutId(id); | ||
const id = setTimeout(() => timeoutCallback.current(...args), waitMs); | ||
timeoutIds.current.push(id); | ||
return id; | ||
}, [timeout]); | ||
}, [waitMs]); | ||
} | ||
/* | ||
* This hook was inspired by Dan Abramov's blogpost: | ||
* https://overreacted.io/making-setinterval-declarative-with-react-hooks/ | ||
*/ | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __rest(s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
} | ||
return t; | ||
} | ||
/** | ||
* Calls the given function at a regular interval. | ||
* | ||
* The interval can be paused, resumed, stopped etc. via the returned callbacks. | ||
* | ||
* Active intervals will be cleared in case the component unmounts. | ||
* | ||
* @param callback A function that will be called at the specified interval | ||
* @param delay time in milliseconds between each invocation of callback | ||
* @returns An object of properties to control the interval or see it's status | ||
* This private API hook adds controls like play, pause, start, stop to a hook. | ||
* @param isPausedInitially | ||
* @param isStoppedInitially | ||
*/ | ||
const useInterval = (callback, delay) => { | ||
const [isPaused, setIsPaused] = useState(false); | ||
const [isStopped, setIsStopped] = useState(false); | ||
const intervalActive = useRef(true); | ||
const intervalCallback = useRef(callback); | ||
const intervalId = useRef(null); | ||
const useControls = (isPausedInitially = false, isStoppedInitially = false) => { | ||
const [isPaused, setIsPaused] = useState(isPausedInitially); | ||
const [isStopped, setIsStopped] = useState(isStoppedInitially); | ||
const isPausedRef = useRef(isPausedInitially); | ||
const pause = useCallback(() => { | ||
intervalActive.current = false; | ||
isPausedRef.current = true; | ||
setIsPaused(true); // notify interval owner | ||
}, [setIsPaused]); | ||
const resume = useCallback(() => { | ||
intervalActive.current = true; | ||
isPausedRef.current = false; | ||
setIsPaused(false); // notify interval owner | ||
@@ -101,6 +118,36 @@ }, [setIsPaused]); | ||
const start = useCallback(() => { | ||
intervalActive.current = true; | ||
isPausedRef.current = false; | ||
setIsPaused(false); | ||
setIsStopped(false); | ||
}, [setIsStopped]); | ||
return { | ||
isPaused, | ||
isPausedRef, | ||
isStopped, | ||
pause, | ||
resume, | ||
stop, | ||
start, | ||
}; | ||
}; | ||
/** | ||
* Calls the given function at a regular interval. | ||
* | ||
* The interval can be paused, resumed, stopped etc. via the returned callbacks. | ||
* | ||
* Active intervals will be cleared in case the component unmounts. | ||
* | ||
* @param callback A function that will be called at the specified interval | ||
* @param delay time in milliseconds between each invocation of callback. If set to `null` the interval will come to a halt. | ||
* @param options A set of options to control the interval behaviour. | ||
* @param [options.startOnMount = false] (optional) Defaults to false. If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
* @returns An object of properties to control the interval or see it's status | ||
*/ | ||
const useInterval = (callback, delay, options = {}) => { | ||
const { startOnMount = false } = options; | ||
const _a = useControls(false, !startOnMount), { isPausedRef } = _a, controls = __rest(_a, ["isPausedRef"]); | ||
const { isStopped } = controls; | ||
const intervalCallback = useRef(callback); | ||
const intervalId = useRef(null); | ||
useEffect(() => { | ||
@@ -110,6 +157,6 @@ intervalCallback.current = callback; | ||
const onIntervalStep = useCallback(() => { | ||
if (intervalActive.current === true) { | ||
if (isPausedRef.current === false) { | ||
intervalCallback.current(); | ||
} | ||
}, [intervalCallback, intervalActive]); | ||
}, [intervalCallback, isPausedRef]); | ||
useEffect(() => { | ||
@@ -121,10 +168,3 @@ if (delay !== null && !isStopped) { | ||
}, [delay, isStopped]); | ||
return { | ||
isPaused, | ||
isStopped, | ||
pause, | ||
resume, | ||
stop, | ||
start, | ||
}; | ||
return Object.assign({}, controls); | ||
}; | ||
@@ -137,10 +177,12 @@ | ||
* | ||
* @param settings Counter settings and interval options | ||
* @param settings.start the initial value | ||
* @param settings.interval duration in ms between steps | ||
* @param settings.stepSize amount that is added to the current counter value on every step | ||
* @param [settings.startOnMount = false] If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
*/ | ||
const useCounter = (settings) => { | ||
const { start = 0, interval = 1000, stepSize = 1 } = settings; | ||
const { start = 0, interval = 1000, stepSize = 1 } = settings, intervalOptions = __rest(settings, ["start", "interval", "stepSize"]); | ||
const [val, setVal] = useState(start); | ||
const intervalControls = useInterval(() => setVal(val + stepSize), interval); | ||
const intervalControls = useInterval(() => setVal(val + stepSize), interval, intervalOptions); | ||
return [val, intervalControls]; | ||
@@ -152,8 +194,17 @@ }; | ||
* @param start Starting value of the timer | ||
* @param options A set of interval options, see useInterval(). | ||
* @param [options.startOnMount = false] If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
* @returns the current timer value. | ||
*/ | ||
const useTimer = (start = 0) => { | ||
const [value] = useCounter({ start, interval: 1000, stepSize: 1 }); | ||
return value; | ||
const useTimer = (start = 0, options = {}) => useCounter(Object.assign({ start, interval: 1000, stepSize: 1 }, options)); | ||
const LIB_NAME = 'React Timing Hooks'; | ||
const buildMessage = (message) => { | ||
return `${LIB_NAME}: ${message}`; | ||
}; | ||
const log = (logFunction, message) => { | ||
console[logFunction](buildMessage(message)); | ||
}; | ||
const logError = (message) => log('error', message); | ||
const logWarning = (message) => log('warn', message); | ||
@@ -168,3 +219,3 @@ /** | ||
if (!window.requestIdleCallback) { | ||
console.warn('This browser does not support "requestIdleCallback"'); | ||
logWarning('This browser does not support "requestIdleCallback"'); | ||
return; | ||
@@ -199,3 +250,3 @@ } | ||
if (!window.requestIdleCallback) { | ||
console.warn('This browser does not support "requestIdleCallback"'); | ||
logWarning('This browser does not support "requestIdleCallback"'); | ||
return callback; | ||
@@ -230,3 +281,3 @@ } | ||
const rafCallback = useRef(callback); | ||
const [handle, setHandle] = useState(null); | ||
const handleRef = useRef(null); | ||
useEffect(() => { | ||
@@ -237,32 +288,39 @@ rafCallback.current = callback; | ||
return () => { | ||
if (handle) { | ||
cancelAnimationFrame(handle); | ||
if (handleRef.current) { | ||
cancelAnimationFrame(handleRef.current); | ||
} | ||
}; | ||
}, [handle]); | ||
}, []); | ||
return useCallback((...args) => { | ||
const h = requestAnimationFrame(() => rafCallback.current(...args)); | ||
setHandle(h); | ||
handleRef.current = requestAnimationFrame(() => rafCallback.current(...args)); | ||
return handleRef.current; | ||
}, []); | ||
}; | ||
const useAnimationFrameLoop = (callback, pause = false) => { | ||
const useAnimationFrameLoop = (callback, options = {}) => { | ||
const { startOnMount = false } = options; | ||
const rafCallback = useRef(callback); | ||
const pauseValue = useRef(false); | ||
const _a = useControls(false, !startOnMount), { isPausedRef } = _a, controls = __rest(_a, ["isPausedRef"]); | ||
const { isStopped } = controls; | ||
useEffect(() => { | ||
rafCallback.current = callback; | ||
pauseValue.current = pause; | ||
}, [callback, pause]); | ||
}, [callback]); | ||
const nextCallback = useCallback(() => { | ||
if (!pauseValue.current) { | ||
rafCallback.current(); | ||
if (!isStopped) { | ||
if (!isPausedRef.current) { | ||
rafCallback.current(); | ||
} | ||
runInLoop(); | ||
} | ||
}, []); | ||
}, [isStopped]); | ||
const runInLoop = useAnimationFrame(nextCallback); | ||
useEffect(() => { | ||
if (!pause) { | ||
runInLoop(); | ||
if (!isStopped) { | ||
const h = runInLoop(); | ||
return () => { | ||
cancelAnimationFrame(h); | ||
}; | ||
} | ||
}, [runInLoop, pause]); | ||
}, [runInLoop, isStopped]); | ||
return controls; | ||
}; | ||
@@ -277,22 +335,101 @@ | ||
* @param options options.locales and options.dateTimeFormatOptions will be directly forwarded to date.toLocaleTimeString(). You can also use options.customFormatter to override the output of the hook. The output must match the generic type of the hook. | ||
* @returns {T} The current (formatted) time | ||
* @returns The current (formatted) time | ||
*/ | ||
const useClock = (options) => { | ||
const startTimeInSeconds = ((options === null || options === void 0 ? void 0 : options.startTimeInMilliseconds) || Date.now()) / 1000; | ||
const currentTimeInSeconds = useTimer(startTimeInSeconds); | ||
const startTimeMs = (options === null || options === void 0 ? void 0 : options.startTimeInMilliseconds) || Date.now(); | ||
const startTimeInSeconds = startTimeMs / 1000; | ||
const [currentTimeInSeconds, controls] = useTimer(startTimeInSeconds, { | ||
startOnMount: true, | ||
}); | ||
const date = new Date(currentTimeInSeconds * 1000); | ||
if (options === null || options === void 0 ? void 0 : options.customFormatter) { | ||
return options === null || options === void 0 ? void 0 : options.customFormatter(date); | ||
} | ||
return date.toLocaleTimeString(options === null || options === void 0 ? void 0 : options.locales, options === null || options === void 0 ? void 0 : options.dateTimeFormatOptions); | ||
const formattedTime = (options === null || options === void 0 ? void 0 : options.customFormatter) | ||
? options === null || options === void 0 ? void 0 : options.customFormatter(date) | ||
: date.toLocaleTimeString(options === null || options === void 0 ? void 0 : options.locales, options === null || options === void 0 ? void 0 : options.dateTimeFormatOptions); | ||
return [formattedTime, controls]; | ||
}; | ||
/** | ||
* This hook creates a countdown that starts at the specified number and decreases this number each second. | ||
* The current value of the countdown is returned from the hook. The countdown will start immediately on creation. | ||
* @param start Starting value of the countdown | ||
* This hook creates a countdown that starts and ends at the specified numbers. | ||
* By default, the value will be changed every second and count downwards. Both properties can be changed, though. | ||
* | ||
* The user will be notified when the countdown ends via the event callback `options.onEnd()`. | ||
* | ||
* Use the returned `start()` callback or set `options.startOnMount` to start the countdown. | ||
* | ||
* @param from Start value | ||
* @param to End value. Will trigger the `options.onEnd()` callback. | ||
* @param options A set of countdown options. | ||
* @param options.onEnd If set, this callback will be called when the 2nd param "`to`" is reached. | ||
* @param [options.startOnMount = false] If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
* @returns an array: the first element is the countdown value, the second is an object of interval controls, see useInterval() | ||
*/ | ||
const useCountdown = (start) => useCounter({ start, interval: 1000, stepSize: -1 }); | ||
const useCountdown = (from, to, options = {}) => { | ||
const onEndCallback = useRef(options.onEnd); | ||
const [value, counterControls] = useCounter(Object.assign({ start: from, interval: 1000, stepSize: -1 }, options)); | ||
useEffect(() => { | ||
onEndCallback.current = options.onEnd; | ||
}, [options.onEnd]); | ||
useEffect(() => { | ||
if (to > from && | ||
(typeof options.stepSize === 'undefined' || options.stepSize < 0)) { | ||
logError(`Stopped countdown because a countdown from ${from} to ${to} will never end`); | ||
counterControls.stop(); | ||
} | ||
}, [from, to]); | ||
useEffect(() => { | ||
var _a; | ||
if (value === to) { | ||
counterControls.stop(); | ||
(_a = onEndCallback.current) === null || _a === void 0 ? void 0 : _a.call(onEndCallback); | ||
} | ||
}, [value]); | ||
return [value, counterControls]; | ||
}; | ||
export { useAnimationFrame, useAnimationFrameLoop, useClock, useCountdown, useCounter, useIdleCallback, useIdleCallbackEffect, useInterval, useTimeout, useTimeoutEffect, useTimer }; | ||
/** | ||
* Debounces a callback. | ||
* | ||
* By default, `options.trailing = true`, meaning that the callback will only be invoked as soon as a certain time | ||
* has passed since the last call (defined by `waitMs`). | ||
* | ||
* Alternatively, the function can also be called immediately and then be blocked on consecutive calls until the timeout | ||
* is over (when `options.leading` is true). You can also set both `options.leading` and `options.trailing` to `true`. | ||
* | ||
* @param callback | ||
* @param waitMs The minimum time until an invocation can happen. | ||
* @param options | ||
* @param [options.leading = false] If true, invoke before the timeout | ||
* @param [options.trailing = true] If true, invoke after the timeout | ||
*/ | ||
function useDebounce(callback, waitMs, options = {}) { | ||
const { leading = false, trailing = true } = options; | ||
const debouncedCallback = useRef(callback); | ||
const timeoutId = useRef(null); | ||
useEffect(() => { | ||
debouncedCallback.current = callback; | ||
}, [callback]); | ||
useEffect(() => { | ||
return () => { | ||
if (timeoutId.current) { | ||
clearTimeout(timeoutId.current); | ||
} | ||
}; | ||
}, []); | ||
return useCallback((...args) => { | ||
if (timeoutId.current) { | ||
clearTimeout(timeoutId.current); | ||
} | ||
else if (leading) { | ||
debouncedCallback.current(...args); | ||
} | ||
timeoutId.current = setTimeout(() => { | ||
if (trailing) { | ||
debouncedCallback.current(...args); | ||
} | ||
timeoutId.current = null; | ||
}, waitMs); | ||
return timeoutId.current; | ||
}, [waitMs]); | ||
} | ||
export { useAnimationFrame, useAnimationFrameLoop, useClock, useCountdown, useCounter, useDebounce, useIdleCallback, useIdleCallbackEffect, useInterval, useTimeout, useTimeoutEffect, useTimer }; |
@@ -43,12 +43,16 @@ 'use strict'; | ||
* | ||
* Pending callbacks will be cleared in case the component unmounts. | ||
* This **will not debounce or throttle** the callbacks, i.e. consecutive calls of this function will all spawn | ||
* new timeouts even if some are still pending. If you want a debouncing version, take a look at `useDebounce()`. | ||
* If you want a throttling version, see `useThrottle()`. | ||
* | ||
* Pending callbacks will only(!) be cleared in case the component unmounts. | ||
* | ||
* @param callback The callback that is invoked after the timeout expired | ||
* @param timeout A timeout in milliseconds | ||
* @param waitMs The timeout in milliseconds | ||
* | ||
* @returns a function that executes the provided callback after the specified amount of time | ||
*/ | ||
function useTimeout(callback, timeout) { | ||
function useTimeout(callback, waitMs) { | ||
const timeoutCallback = react.useRef(callback); | ||
const [timeoutId, setTimeoutId] = react.useState(null); | ||
const timeoutIds = react.useRef([]); | ||
react.useEffect(() => { | ||
@@ -59,41 +63,54 @@ timeoutCallback.current = callback; | ||
return () => { | ||
if (timeoutId) { | ||
clearTimeout(timeoutId); | ||
} | ||
timeoutIds.current.forEach((id) => clearTimeout(id)); | ||
}; | ||
}, [timeoutId]); | ||
}, [timeoutIds]); | ||
return react.useCallback((...args) => { | ||
const id = setTimeout(() => timeoutCallback.current(...args), timeout); | ||
setTimeoutId(id); | ||
const id = setTimeout(() => timeoutCallback.current(...args), waitMs); | ||
timeoutIds.current.push(id); | ||
return id; | ||
}, [timeout]); | ||
}, [waitMs]); | ||
} | ||
/* | ||
* This hook was inspired by Dan Abramov's blogpost: | ||
* https://overreacted.io/making-setinterval-declarative-with-react-hooks/ | ||
*/ | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __rest(s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
} | ||
return t; | ||
} | ||
/** | ||
* Calls the given function at a regular interval. | ||
* | ||
* The interval can be paused, resumed, stopped etc. via the returned callbacks. | ||
* | ||
* Active intervals will be cleared in case the component unmounts. | ||
* | ||
* @param callback A function that will be called at the specified interval | ||
* @param delay time in milliseconds between each invocation of callback | ||
* @returns An object of properties to control the interval or see it's status | ||
* This private API hook adds controls like play, pause, start, stop to a hook. | ||
* @param isPausedInitially | ||
* @param isStoppedInitially | ||
*/ | ||
const useInterval = (callback, delay) => { | ||
const [isPaused, setIsPaused] = react.useState(false); | ||
const [isStopped, setIsStopped] = react.useState(false); | ||
const intervalActive = react.useRef(true); | ||
const intervalCallback = react.useRef(callback); | ||
const intervalId = react.useRef(null); | ||
const useControls = (isPausedInitially = false, isStoppedInitially = false) => { | ||
const [isPaused, setIsPaused] = react.useState(isPausedInitially); | ||
const [isStopped, setIsStopped] = react.useState(isStoppedInitially); | ||
const isPausedRef = react.useRef(isPausedInitially); | ||
const pause = react.useCallback(() => { | ||
intervalActive.current = false; | ||
isPausedRef.current = true; | ||
setIsPaused(true); // notify interval owner | ||
}, [setIsPaused]); | ||
const resume = react.useCallback(() => { | ||
intervalActive.current = true; | ||
isPausedRef.current = false; | ||
setIsPaused(false); // notify interval owner | ||
@@ -105,6 +122,36 @@ }, [setIsPaused]); | ||
const start = react.useCallback(() => { | ||
intervalActive.current = true; | ||
isPausedRef.current = false; | ||
setIsPaused(false); | ||
setIsStopped(false); | ||
}, [setIsStopped]); | ||
return { | ||
isPaused, | ||
isPausedRef, | ||
isStopped, | ||
pause, | ||
resume, | ||
stop, | ||
start, | ||
}; | ||
}; | ||
/** | ||
* Calls the given function at a regular interval. | ||
* | ||
* The interval can be paused, resumed, stopped etc. via the returned callbacks. | ||
* | ||
* Active intervals will be cleared in case the component unmounts. | ||
* | ||
* @param callback A function that will be called at the specified interval | ||
* @param delay time in milliseconds between each invocation of callback. If set to `null` the interval will come to a halt. | ||
* @param options A set of options to control the interval behaviour. | ||
* @param [options.startOnMount = false] (optional) Defaults to false. If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
* @returns An object of properties to control the interval or see it's status | ||
*/ | ||
const useInterval = (callback, delay, options = {}) => { | ||
const { startOnMount = false } = options; | ||
const _a = useControls(false, !startOnMount), { isPausedRef } = _a, controls = __rest(_a, ["isPausedRef"]); | ||
const { isStopped } = controls; | ||
const intervalCallback = react.useRef(callback); | ||
const intervalId = react.useRef(null); | ||
react.useEffect(() => { | ||
@@ -114,6 +161,6 @@ intervalCallback.current = callback; | ||
const onIntervalStep = react.useCallback(() => { | ||
if (intervalActive.current === true) { | ||
if (isPausedRef.current === false) { | ||
intervalCallback.current(); | ||
} | ||
}, [intervalCallback, intervalActive]); | ||
}, [intervalCallback, isPausedRef]); | ||
react.useEffect(() => { | ||
@@ -125,10 +172,3 @@ if (delay !== null && !isStopped) { | ||
}, [delay, isStopped]); | ||
return { | ||
isPaused, | ||
isStopped, | ||
pause, | ||
resume, | ||
stop, | ||
start, | ||
}; | ||
return Object.assign({}, controls); | ||
}; | ||
@@ -141,10 +181,12 @@ | ||
* | ||
* @param settings Counter settings and interval options | ||
* @param settings.start the initial value | ||
* @param settings.interval duration in ms between steps | ||
* @param settings.stepSize amount that is added to the current counter value on every step | ||
* @param [settings.startOnMount = false] If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
*/ | ||
const useCounter = (settings) => { | ||
const { start = 0, interval = 1000, stepSize = 1 } = settings; | ||
const { start = 0, interval = 1000, stepSize = 1 } = settings, intervalOptions = __rest(settings, ["start", "interval", "stepSize"]); | ||
const [val, setVal] = react.useState(start); | ||
const intervalControls = useInterval(() => setVal(val + stepSize), interval); | ||
const intervalControls = useInterval(() => setVal(val + stepSize), interval, intervalOptions); | ||
return [val, intervalControls]; | ||
@@ -156,8 +198,17 @@ }; | ||
* @param start Starting value of the timer | ||
* @param options A set of interval options, see useInterval(). | ||
* @param [options.startOnMount = false] If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
* @returns the current timer value. | ||
*/ | ||
const useTimer = (start = 0) => { | ||
const [value] = useCounter({ start, interval: 1000, stepSize: 1 }); | ||
return value; | ||
const useTimer = (start = 0, options = {}) => useCounter(Object.assign({ start, interval: 1000, stepSize: 1 }, options)); | ||
const LIB_NAME = 'React Timing Hooks'; | ||
const buildMessage = (message) => { | ||
return `${LIB_NAME}: ${message}`; | ||
}; | ||
const log = (logFunction, message) => { | ||
console[logFunction](buildMessage(message)); | ||
}; | ||
const logError = (message) => log('error', message); | ||
const logWarning = (message) => log('warn', message); | ||
@@ -172,3 +223,3 @@ /** | ||
if (!window.requestIdleCallback) { | ||
console.warn('This browser does not support "requestIdleCallback"'); | ||
logWarning('This browser does not support "requestIdleCallback"'); | ||
return; | ||
@@ -203,3 +254,3 @@ } | ||
if (!window.requestIdleCallback) { | ||
console.warn('This browser does not support "requestIdleCallback"'); | ||
logWarning('This browser does not support "requestIdleCallback"'); | ||
return callback; | ||
@@ -234,3 +285,3 @@ } | ||
const rafCallback = react.useRef(callback); | ||
const [handle, setHandle] = react.useState(null); | ||
const handleRef = react.useRef(null); | ||
react.useEffect(() => { | ||
@@ -241,32 +292,39 @@ rafCallback.current = callback; | ||
return () => { | ||
if (handle) { | ||
cancelAnimationFrame(handle); | ||
if (handleRef.current) { | ||
cancelAnimationFrame(handleRef.current); | ||
} | ||
}; | ||
}, [handle]); | ||
}, []); | ||
return react.useCallback((...args) => { | ||
const h = requestAnimationFrame(() => rafCallback.current(...args)); | ||
setHandle(h); | ||
handleRef.current = requestAnimationFrame(() => rafCallback.current(...args)); | ||
return handleRef.current; | ||
}, []); | ||
}; | ||
const useAnimationFrameLoop = (callback, pause = false) => { | ||
const useAnimationFrameLoop = (callback, options = {}) => { | ||
const { startOnMount = false } = options; | ||
const rafCallback = react.useRef(callback); | ||
const pauseValue = react.useRef(false); | ||
const _a = useControls(false, !startOnMount), { isPausedRef } = _a, controls = __rest(_a, ["isPausedRef"]); | ||
const { isStopped } = controls; | ||
react.useEffect(() => { | ||
rafCallback.current = callback; | ||
pauseValue.current = pause; | ||
}, [callback, pause]); | ||
}, [callback]); | ||
const nextCallback = react.useCallback(() => { | ||
if (!pauseValue.current) { | ||
rafCallback.current(); | ||
if (!isStopped) { | ||
if (!isPausedRef.current) { | ||
rafCallback.current(); | ||
} | ||
runInLoop(); | ||
} | ||
}, []); | ||
}, [isStopped]); | ||
const runInLoop = useAnimationFrame(nextCallback); | ||
react.useEffect(() => { | ||
if (!pause) { | ||
runInLoop(); | ||
if (!isStopped) { | ||
const h = runInLoop(); | ||
return () => { | ||
cancelAnimationFrame(h); | ||
}; | ||
} | ||
}, [runInLoop, pause]); | ||
}, [runInLoop, isStopped]); | ||
return controls; | ||
}; | ||
@@ -281,22 +339,101 @@ | ||
* @param options options.locales and options.dateTimeFormatOptions will be directly forwarded to date.toLocaleTimeString(). You can also use options.customFormatter to override the output of the hook. The output must match the generic type of the hook. | ||
* @returns {T} The current (formatted) time | ||
* @returns The current (formatted) time | ||
*/ | ||
const useClock = (options) => { | ||
const startTimeInSeconds = ((options === null || options === void 0 ? void 0 : options.startTimeInMilliseconds) || Date.now()) / 1000; | ||
const currentTimeInSeconds = useTimer(startTimeInSeconds); | ||
const startTimeMs = (options === null || options === void 0 ? void 0 : options.startTimeInMilliseconds) || Date.now(); | ||
const startTimeInSeconds = startTimeMs / 1000; | ||
const [currentTimeInSeconds, controls] = useTimer(startTimeInSeconds, { | ||
startOnMount: true, | ||
}); | ||
const date = new Date(currentTimeInSeconds * 1000); | ||
if (options === null || options === void 0 ? void 0 : options.customFormatter) { | ||
return options === null || options === void 0 ? void 0 : options.customFormatter(date); | ||
} | ||
return date.toLocaleTimeString(options === null || options === void 0 ? void 0 : options.locales, options === null || options === void 0 ? void 0 : options.dateTimeFormatOptions); | ||
const formattedTime = (options === null || options === void 0 ? void 0 : options.customFormatter) | ||
? options === null || options === void 0 ? void 0 : options.customFormatter(date) | ||
: date.toLocaleTimeString(options === null || options === void 0 ? void 0 : options.locales, options === null || options === void 0 ? void 0 : options.dateTimeFormatOptions); | ||
return [formattedTime, controls]; | ||
}; | ||
/** | ||
* This hook creates a countdown that starts at the specified number and decreases this number each second. | ||
* The current value of the countdown is returned from the hook. The countdown will start immediately on creation. | ||
* @param start Starting value of the countdown | ||
* This hook creates a countdown that starts and ends at the specified numbers. | ||
* By default, the value will be changed every second and count downwards. Both properties can be changed, though. | ||
* | ||
* The user will be notified when the countdown ends via the event callback `options.onEnd()`. | ||
* | ||
* Use the returned `start()` callback or set `options.startOnMount` to start the countdown. | ||
* | ||
* @param from Start value | ||
* @param to End value. Will trigger the `options.onEnd()` callback. | ||
* @param options A set of countdown options. | ||
* @param options.onEnd If set, this callback will be called when the 2nd param "`to`" is reached. | ||
* @param [options.startOnMount = false] If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
* @returns an array: the first element is the countdown value, the second is an object of interval controls, see useInterval() | ||
*/ | ||
const useCountdown = (start) => useCounter({ start, interval: 1000, stepSize: -1 }); | ||
const useCountdown = (from, to, options = {}) => { | ||
const onEndCallback = react.useRef(options.onEnd); | ||
const [value, counterControls] = useCounter(Object.assign({ start: from, interval: 1000, stepSize: -1 }, options)); | ||
react.useEffect(() => { | ||
onEndCallback.current = options.onEnd; | ||
}, [options.onEnd]); | ||
react.useEffect(() => { | ||
if (to > from && | ||
(typeof options.stepSize === 'undefined' || options.stepSize < 0)) { | ||
logError(`Stopped countdown because a countdown from ${from} to ${to} will never end`); | ||
counterControls.stop(); | ||
} | ||
}, [from, to]); | ||
react.useEffect(() => { | ||
var _a; | ||
if (value === to) { | ||
counterControls.stop(); | ||
(_a = onEndCallback.current) === null || _a === void 0 ? void 0 : _a.call(onEndCallback); | ||
} | ||
}, [value]); | ||
return [value, counterControls]; | ||
}; | ||
/** | ||
* Debounces a callback. | ||
* | ||
* By default, `options.trailing = true`, meaning that the callback will only be invoked as soon as a certain time | ||
* has passed since the last call (defined by `waitMs`). | ||
* | ||
* Alternatively, the function can also be called immediately and then be blocked on consecutive calls until the timeout | ||
* is over (when `options.leading` is true). You can also set both `options.leading` and `options.trailing` to `true`. | ||
* | ||
* @param callback | ||
* @param waitMs The minimum time until an invocation can happen. | ||
* @param options | ||
* @param [options.leading = false] If true, invoke before the timeout | ||
* @param [options.trailing = true] If true, invoke after the timeout | ||
*/ | ||
function useDebounce(callback, waitMs, options = {}) { | ||
const { leading = false, trailing = true } = options; | ||
const debouncedCallback = react.useRef(callback); | ||
const timeoutId = react.useRef(null); | ||
react.useEffect(() => { | ||
debouncedCallback.current = callback; | ||
}, [callback]); | ||
react.useEffect(() => { | ||
return () => { | ||
if (timeoutId.current) { | ||
clearTimeout(timeoutId.current); | ||
} | ||
}; | ||
}, []); | ||
return react.useCallback((...args) => { | ||
if (timeoutId.current) { | ||
clearTimeout(timeoutId.current); | ||
} | ||
else if (leading) { | ||
debouncedCallback.current(...args); | ||
} | ||
timeoutId.current = setTimeout(() => { | ||
if (trailing) { | ||
debouncedCallback.current(...args); | ||
} | ||
timeoutId.current = null; | ||
}, waitMs); | ||
return timeoutId.current; | ||
}, [waitMs]); | ||
} | ||
exports.useAnimationFrame = useAnimationFrame; | ||
@@ -307,2 +444,3 @@ exports.useAnimationFrameLoop = useAnimationFrameLoop; | ||
exports.useCounter = useCounter; | ||
exports.useDebounce = useDebounce; | ||
exports.useIdleCallback = useIdleCallback; | ||
@@ -309,0 +447,0 @@ exports.useIdleCallbackEffect = useIdleCallbackEffect; |
@@ -0,1 +1,2 @@ | ||
import { IntervalControls } from './useInterval'; | ||
export interface ClockOptions<T> { | ||
@@ -14,5 +15,5 @@ locales?: string | string[]; | ||
* @param options options.locales and options.dateTimeFormatOptions will be directly forwarded to date.toLocaleTimeString(). You can also use options.customFormatter to override the output of the hook. The output must match the generic type of the hook. | ||
* @returns {T} The current (formatted) time | ||
* @returns The current (formatted) time | ||
*/ | ||
declare const useClock: <T = string>(options?: ClockOptions<T> | undefined) => string | T; | ||
declare const useClock: <T = string>(options?: ClockOptions<T> | undefined) => [T, IntervalControls]; | ||
export default useClock; |
@@ -0,9 +1,22 @@ | ||
import { CounterSettings } from './useCounter'; | ||
import { IntervalControls } from './useInterval'; | ||
export type CountdownOptions = Partial<CounterSettings> & { | ||
onEnd?: () => unknown; | ||
}; | ||
/** | ||
* This hook creates a countdown that starts at the specified number and decreases this number each second. | ||
* The current value of the countdown is returned from the hook. The countdown will start immediately on creation. | ||
* @param start Starting value of the countdown | ||
* This hook creates a countdown that starts and ends at the specified numbers. | ||
* By default, the value will be changed every second and count downwards. Both properties can be changed, though. | ||
* | ||
* The user will be notified when the countdown ends via the event callback `options.onEnd()`. | ||
* | ||
* Use the returned `start()` callback or set `options.startOnMount` to start the countdown. | ||
* | ||
* @param from Start value | ||
* @param to End value. Will trigger the `options.onEnd()` callback. | ||
* @param options A set of countdown options. | ||
* @param options.onEnd If set, this callback will be called when the 2nd param "`to`" is reached. | ||
* @param [options.startOnMount = false] If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
* @returns an array: the first element is the countdown value, the second is an object of interval controls, see useInterval() | ||
*/ | ||
declare const useCountdown: (start: number) => [number, IntervalControls]; | ||
declare const useCountdown: (from: number, to: number, options?: CountdownOptions) => [number, IntervalControls]; | ||
export default useCountdown; |
@@ -1,7 +0,7 @@ | ||
import { IntervalControls } from './useInterval'; | ||
type CounterSettings = { | ||
import { IntervalControls, IntervalOptions } from './useInterval'; | ||
export type CounterSettings = { | ||
start: number; | ||
interval: number; | ||
stepSize: number; | ||
}; | ||
} & IntervalOptions; | ||
/** | ||
@@ -12,7 +12,9 @@ * A hook that updates a number on a regular interval based on the provided settings. | ||
* | ||
* @param settings Counter settings and interval options | ||
* @param settings.start the initial value | ||
* @param settings.interval duration in ms between steps | ||
* @param settings.stepSize amount that is added to the current counter value on every step | ||
* @param [settings.startOnMount = false] If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
*/ | ||
declare const useCounter: (settings: CounterSettings) => [number, IntervalControls]; | ||
export default useCounter; |
@@ -1,8 +0,5 @@ | ||
export type IntervalControls = { | ||
isPaused: boolean; | ||
isStopped: boolean; | ||
pause: () => void; | ||
resume: () => void; | ||
stop: () => void; | ||
start: () => void; | ||
import { Controls } from '../controls/useControls'; | ||
export type IntervalControls = Omit<Controls, 'isPausedRef'>; | ||
export type IntervalOptions = { | ||
startOnMount?: boolean; | ||
}; | ||
@@ -17,6 +14,8 @@ /** | ||
* @param callback A function that will be called at the specified interval | ||
* @param delay time in milliseconds between each invocation of callback | ||
* @param delay time in milliseconds between each invocation of callback. If set to `null` the interval will come to a halt. | ||
* @param options A set of options to control the interval behaviour. | ||
* @param [options.startOnMount = false] (optional) Defaults to false. If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
* @returns An object of properties to control the interval or see it's status | ||
*/ | ||
declare const useInterval: <T extends (...args: never[]) => unknown>(callback: T, delay: number | null) => IntervalControls; | ||
declare const useInterval: <T extends Function>(callback: T, delay: number | null, options?: IntervalOptions) => IntervalControls; | ||
export default useInterval; |
@@ -0,7 +1,10 @@ | ||
import { IntervalControls, IntervalOptions } from './useInterval'; | ||
/** | ||
* A hook that starts a timer, i.e. a reactive number that is increased every second. | ||
* @param start Starting value of the timer | ||
* @param options A set of interval options, see useInterval(). | ||
* @param [options.startOnMount = false] If true, the interval will immediately start on mount. If false, it has to be started manually via `start()`. | ||
* @returns the current timer value. | ||
*/ | ||
declare const useTimer: (start?: number) => number; | ||
declare const useTimer: (start?: number, options?: IntervalOptions) => [number, IntervalControls]; | ||
export default useTimer; |
@@ -5,10 +5,14 @@ /// <reference types="node" /> | ||
* | ||
* Pending callbacks will be cleared in case the component unmounts. | ||
* This **will not debounce or throttle** the callbacks, i.e. consecutive calls of this function will all spawn | ||
* new timeouts even if some are still pending. If you want a debouncing version, take a look at `useDebounce()`. | ||
* If you want a throttling version, see `useThrottle()`. | ||
* | ||
* Pending callbacks will only(!) be cleared in case the component unmounts. | ||
* | ||
* @param callback The callback that is invoked after the timeout expired | ||
* @param timeout A timeout in milliseconds | ||
* @param waitMs The timeout in milliseconds | ||
* | ||
* @returns a function that executes the provided callback after the specified amount of time | ||
*/ | ||
declare function useTimeout<T extends (...args: never[]) => unknown>(callback: T, timeout: number): (...args: Parameters<T>) => NodeJS.Timeout | number; | ||
declare function useTimeout<T extends (...args: never[]) => unknown>(callback: T, waitMs: number): (...args: Parameters<T>) => NodeJS.Timeout | number; | ||
export default useTimeout; |
{ | ||
"name": "react-timing-hooks", | ||
"version": "3.2.2", | ||
"version": "4.0.0", | ||
"description": "React hooks for setTimeout, setInterval, requestAnimationFrame, requestIdleCallback", | ||
@@ -12,7 +12,7 @@ "main": "dist/index.js", | ||
"test:unit": "jest src", | ||
"test:integration": "rimraf \"./tmp\" && npm run build && jest integration-tests", | ||
"test:integration": "npm run build && jest integration-tests", | ||
"commit": "git-cz", | ||
"prettier:check": "prettier --list-different \"{src,integration-tests}/**/*.{ts,tsx}\"", | ||
"prettier:write": "prettier --write \"{src,integration-tests}/**/*.{ts,tsx}\"", | ||
"build": "rollup -c", | ||
"build": "rimraf \"./tmp\" && rollup -c", | ||
"build:watch": "npm run build -- --watch", | ||
@@ -39,4 +39,8 @@ "build:prod": "rimraf \"./dist\" && rollup -c rollup.config.prod.js", | ||
"requestIdleCallback", | ||
"rate-limiting", | ||
"clock", | ||
"timer" | ||
"timer", | ||
"throttle", | ||
"debounce", | ||
"time" | ||
], | ||
@@ -43,0 +47,0 @@ "author": "Eric Lambrecht", |
@@ -10,3 +10,5 @@ <img alt="logo" src="https://github.com/EricLambrecht/react-timing-hooks/raw/main/logo.png" width="680" /> | ||
This is a very little package with **React hooks wrapping time-related Vanilla JS functions**, so you can use them with minimal effort in your React apps without having to worry about manual clean up, testing, or typing (if you use Typescript). | ||
This is a very little package with **React hooks wrapping time-related Vanilla JS functions**, | ||
so you can use them with minimal effort in your React apps without having to worry about manual | ||
clean up, or writing code to pause/resume intervals etc. | ||
@@ -16,12 +18,18 @@ ### Feature Overview | ||
* Several React hooks **wrapping Vanilla JS functions** like: | ||
* `requestAnimationFrame()` | ||
* `setTimeout()` | ||
* `setInterval()` | ||
* `requestIdleCallback()` | ||
* Ability to **pause and resume intervals** | ||
* Additional **utility hooks** for timers, countdowns, display of time, or rendering (e.g. `useAnimationFrameLoop`) | ||
* A **versatile API**: customizable settings, different versions of the same hook (e.g. "useEffect" and "useCallback" versions). | ||
* Quality of Life: **Automatic clean-ups** of pending timers, intervals etc. (e.g. if your component un-mounts before a timer triggers), callbacks are **automatically memoized** | ||
* [`useInterval()`][interval-api] | ||
* [`useTimeout()`][timeout-api] | ||
* [`useAnimationFrame()`][raf-api] | ||
* [`useIdleCallback()`][idle-cb-api], | ||
* β¦and **additional [utility hooks][all-hooks]** for things like | ||
* rate-limiting: `useDebounce()`, `useThrottle()` | ||
* rendering: `useAnimationFrameLoop()` | ||
* counters: `useCounter()`, `useCountdown()`, `useTimer()` | ||
* time: `useClock()` | ||
* effects: `useTimeoutEffect()`, `useIdleCallbackEffect()` | ||
* Ability to **pause, resume, start or stop intervals** | ||
* A **versatile API**: customizable settings, many hook "flavors" depending on the use-case. | ||
* **Automatic clean-ups** of pending timers, intervals etc. | ||
* Callbacks are **automatically memoized** | ||
* Full **Typescript** support | ||
* **[Lightweight](https://bundlephobia.com/result?p=react-timing-hooks)** (ca. 1KB minzipped, no transitive dependencies!) | ||
* **[Lightweight](https://bundlephobia.com/result?p=react-timing-hooks)** (less than 2KB minzipped, no transitive dependencies!) | ||
* **Tree-shakable** β You only bundle what you use! | ||
@@ -44,17 +52,21 @@ | ||
#### Migrate from v3 to v4 | ||
https://ericlambrecht.github.io/react-timing-hooks/migrations/ | ||
## Examples | ||
#### Debouncing a button click with `useTimeout()` | ||
#### A "status logger" with `useInterval()` | ||
```jsx harmony | ||
import { useState } from 'react' | ||
import { useTimeout } from 'react-timing-hooks' | ||
import { useInterval } from 'react-timing-hooks' | ||
const HelloWorld = () => { | ||
const [output, setOutput] = useState(null) | ||
const onButtonClick = useTimeout(() => setOutput('Hello World'), 1000) | ||
const StatusLogger = () => { | ||
const logUpdates = () => console.log('status update') | ||
// could also be intialized with { startOnMount: true } to immediately start the interval | ||
const { start, pause, resume, isPaused } = useInterval(logUpdates, 1000) | ||
return <div> | ||
<button onClick={onButtonClick}>Start timeout!</button> | ||
<p>{output}</p> | ||
<button onClick={start}>Do stuff</button> | ||
<button onClick={isPaused ? resume : pause}>Toggle Status Updates</button> | ||
</div> | ||
@@ -64,12 +76,16 @@ } | ||
#### A resumable interval with `useInterval()` | ||
#### Throttle a button click with `useThrottle()` | ||
```jsx harmony | ||
import { useState } from 'react' | ||
import { useInterval } from 'react-timing-hooks' | ||
import { useThrottle } from 'react-timing-hooks' | ||
const StatusLogger = () => { | ||
const { isPaused, pause, resume } = useInterval(() => console.log('status update'), 1000) | ||
const HelloWorld = () => { | ||
const [result, setResult] = useState(null) | ||
const printResult = () => setOutput(extremeMegaCalculation()) | ||
const onButtonClick = useThrottle(printResult, 1000) | ||
return <div> | ||
<button onClick={isPaused ? resume : pause}>Toggle Status Update</button> | ||
<button onClick={onButtonClick}>Spam me!</button> | ||
<p>Result: {result}</p> | ||
</div> | ||
@@ -79,3 +95,3 @@ } | ||
#### Display how long the user has been browsing using `useTimer()` | ||
#### Display the user's browsing time using `useTimer()` | ||
```jsx harmony | ||
@@ -86,3 +102,3 @@ import { useState } from 'react' | ||
const BrowsingTime = () => { | ||
const elapsedSeconds = useTimer() | ||
const [elapsedSeconds] = useTimer(0, { startOnMount: true }) | ||
return <span>You've been browsing this page for {elapsedSeconds} seconds.</span> | ||
@@ -100,3 +116,3 @@ } | ||
// The displayed time will update every second | ||
const currentTime = useClock() | ||
const [currentTime] = useClock() | ||
return <span>{currentTime}</span> | ||
@@ -106,10 +122,9 @@ } | ||
#### Create canvas renderer using the animation frame loop hook | ||
#### A canvas renderer using `useAnimationFrameLoop()` | ||
```jsx harmony | ||
import { useState } from 'react' | ||
import { useRef } from 'react' | ||
import { useAnimationFrameLoop } from 'react-timing-hooks' | ||
const Renderer = () => { | ||
const [stop, setStop] = useState(false) | ||
const delta = useRef(0) | ||
@@ -125,11 +140,11 @@ const canvasRef = useRef(null) | ||
useAnimationFrameLoop(() => { | ||
const { start, stop, isStopped } = useAnimationFrameLoop(() => { | ||
delta.current += 1 | ||
updateCanvas(delta.current) | ||
}, stop) | ||
}) | ||
return <> | ||
<canvas ref={canvasRef} {...props}/> | ||
<button onClick={() => setStop(!stop)}> | ||
Stop rendering | ||
<button onClick={isStopped ? start : stop}> | ||
{isStopped ? "Start rendering" : "Stop rendering"} | ||
</button> | ||
@@ -278,2 +293,12 @@ </> | ||
see [CONTRIBUTING.md](https://github.com/EricLambrecht/react-timing-hooks/blob/master/CONTRIBUTING.md) | ||
see [CONTRIBUTING.md](https://github.com/EricLambrecht/react-timing-hooks/blob/main/CONTRIBUTING.md) | ||
[timeout-mdn]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout | ||
[interval-mdn]: https://developer.mozilla.org/en-US/docs/Web/API/setInterval | ||
[raf-mdn]: https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame | ||
[idle-cb-mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback | ||
[timeout-api]: https://ericlambrecht.github.io/react-timing-hooks/timeouts-api/ | ||
[interval-api]: https://ericlambrecht.github.io/react-timing-hooks/intervals-api/ | ||
[raf-api]: https://ericlambrecht.github.io/react-timing-hooks/animation-api/ | ||
[idle-cb-api]: https://ericlambrecht.github.io/react-timing-hooks/idle-callback-api/ | ||
[all-hooks]: https://ericlambrecht.github.io/react-timing-hooks/list-of-all-hooks/ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
63098
25
1072
295