@restart/hooks
Advanced tools
Comparing version 0.4.11 to 0.4.12
@@ -0,8 +1,14 @@ | ||
export interface UseDebouncedCallbackOptions { | ||
wait: number; | ||
leading?: boolean; | ||
trailing?: boolean; | ||
maxWait?: number; | ||
} | ||
/** | ||
* Creates a debounced function that will invoke the input function after the | ||
* specified delay. | ||
* specified wait. | ||
* | ||
* @param fn a function that will be debounced | ||
* @param delay The milliseconds delay before invoking the function | ||
* @param waitOrOptions a wait in milliseconds or a debounce configuration | ||
*/ | ||
export default function useDebouncedCallback<TCallback extends (...args: any[]) => any>(fn: TCallback, delay: number): (...args: Parameters<TCallback>) => void; | ||
export default function useDebouncedCallback<TCallback extends (...args: any[]) => any>(fn: TCallback, waitOrOptions: number | UseDebouncedCallbackOptions): (...args: Parameters<TCallback>) => void; |
@@ -10,14 +10,98 @@ "use strict"; | ||
* Creates a debounced function that will invoke the input function after the | ||
* specified delay. | ||
* specified wait. | ||
* | ||
* @param fn a function that will be debounced | ||
* @param delay The milliseconds delay before invoking the function | ||
* @param waitOrOptions a wait in milliseconds or a debounce configuration | ||
*/ | ||
function useDebouncedCallback(fn, delay) { | ||
function useDebouncedCallback(fn, waitOrOptions) { | ||
const lastCallTimeRef = (0, _react.useRef)(null); | ||
const lastInvokeTimeRef = (0, _react.useRef)(0); | ||
const isTimerSetRef = (0, _react.useRef)(false); | ||
const lastArgsRef = (0, _react.useRef)(null); | ||
const { | ||
wait, | ||
maxWait, | ||
leading = false, | ||
trailing = true | ||
} = typeof waitOrOptions === 'number' ? { | ||
wait: waitOrOptions | ||
} : waitOrOptions; | ||
const timeout = (0, _useTimeout.default)(); | ||
return (0, _react.useCallback)((...args) => { | ||
timeout.set(() => { | ||
fn(...args); | ||
}, delay); | ||
}, [fn, delay]); | ||
return (0, _react.useMemo)(() => { | ||
const hasMaxWait = !!maxWait; | ||
function leadingEdge(time) { | ||
// Reset any `maxWait` timer. | ||
lastInvokeTimeRef.current = time; | ||
// Start the timer for the trailing edge. | ||
isTimerSetRef.current = true; | ||
timeout.set(timerExpired, wait); | ||
// Invoke the leading edge. | ||
if (leading) { | ||
invokeFunc(time); | ||
} | ||
} | ||
function trailingEdge(time) { | ||
isTimerSetRef.current = false; | ||
// Only invoke if we have `lastArgs` which means `func` has been | ||
// debounced at least once. | ||
if (trailing && lastArgsRef.current) { | ||
return invokeFunc(time); | ||
} | ||
lastArgsRef.current = null; | ||
} | ||
function timerExpired() { | ||
var _lastCallTimeRef$curr; | ||
var time = Date.now(); | ||
if (shouldInvoke(time)) { | ||
return trailingEdge(time); | ||
} | ||
const timeSinceLastCall = time - ((_lastCallTimeRef$curr = lastCallTimeRef.current) != null ? _lastCallTimeRef$curr : 0); | ||
const timeSinceLastInvoke = time - lastInvokeTimeRef.current; | ||
const timeWaiting = wait - timeSinceLastCall; | ||
// Restart the timer. | ||
timeout.set(timerExpired, hasMaxWait ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting); | ||
} | ||
function invokeFunc(time) { | ||
var _lastArgsRef$current; | ||
const args = (_lastArgsRef$current = lastArgsRef.current) != null ? _lastArgsRef$current : []; | ||
lastArgsRef.current = null; | ||
lastInvokeTimeRef.current = time; | ||
return fn(...args); | ||
} | ||
function shouldInvoke(time) { | ||
var _lastCallTimeRef$curr2; | ||
const timeSinceLastCall = time - ((_lastCallTimeRef$curr2 = lastCallTimeRef.current) != null ? _lastCallTimeRef$curr2 : 0); | ||
const timeSinceLastInvoke = time - lastInvokeTimeRef.current; | ||
// Either this is the first call, activity has stopped and we're at the | ||
// trailing edge, the system time has gone backwards and we're treating | ||
// it as the trailing edge, or we've hit the `maxWait` limit. | ||
return lastCallTimeRef.current === null || timeSinceLastCall >= wait || timeSinceLastCall < 0 || hasMaxWait && timeSinceLastInvoke >= maxWait; | ||
} | ||
return (...args) => { | ||
const time = Date.now(); | ||
const isInvoking = shouldInvoke(time); | ||
lastArgsRef.current = args; | ||
lastCallTimeRef.current = time; | ||
if (isInvoking) { | ||
if (!isTimerSetRef.current) { | ||
return leadingEdge(lastCallTimeRef.current); | ||
} | ||
if (hasMaxWait) { | ||
// Handle invocations in a tight loop. | ||
isTimerSetRef.current = true; | ||
setTimeout(timerExpired, wait); | ||
return invokeFunc(lastCallTimeRef.current); | ||
} | ||
} | ||
if (!isTimerSetRef.current) { | ||
isTimerSetRef.current = true; | ||
setTimeout(timerExpired, wait); | ||
} | ||
}; | ||
}, [fn, wait, maxWait, leading, trailing]); | ||
} |
import { Dispatch, SetStateAction } from 'react'; | ||
import { UseDebouncedCallbackOptions } from './useDebouncedCallback'; | ||
/** | ||
@@ -13,4 +14,4 @@ * Similar to `useState`, except the setter function is debounced by | ||
* @param initialState initial state value | ||
* @param delay The milliseconds delay before a new value is set | ||
* @param delayOrOptions The milliseconds delay before a new value is set, or options object | ||
*/ | ||
export default function useDebouncedState<T>(initialState: T, delay: number): [T, Dispatch<SetStateAction<T>>]; | ||
export default function useDebouncedState<T>(initialState: T, delayOrOptions: number | UseDebouncedCallbackOptions): [T, Dispatch<SetStateAction<T>>]; |
@@ -19,8 +19,8 @@ "use strict"; | ||
* @param initialState initial state value | ||
* @param delay The milliseconds delay before a new value is set | ||
* @param delayOrOptions The milliseconds delay before a new value is set, or options object | ||
*/ | ||
function useDebouncedState(initialState, delay) { | ||
function useDebouncedState(initialState, delayOrOptions) { | ||
const [state, setState] = (0, _react.useState)(initialState); | ||
const debouncedSetState = (0, _useDebouncedCallback.default)(setState, delay); | ||
const debouncedSetState = (0, _useDebouncedCallback.default)(setState, delayOrOptions); | ||
return [state, debouncedSetState]; | ||
} |
@@ -0,1 +1,5 @@ | ||
import { UseDebouncedCallbackOptions } from './useDebouncedCallback'; | ||
export type UseDebouncedValueOptions = UseDebouncedCallbackOptions & { | ||
isEqual?: (a: any, b: any) => boolean; | ||
}; | ||
/** | ||
@@ -7,6 +11,6 @@ * Debounce a value change by a specified number of milliseconds. Useful | ||
* @param value | ||
* @param delayMs | ||
* @param waitOrOptions | ||
* @returns | ||
*/ | ||
declare function useDebouncedValue<TValue>(value: TValue, delayMs?: number): TValue; | ||
declare function useDebouncedValue<TValue>(value: TValue, waitOrOptions?: number | UseDebouncedValueOptions): TValue; | ||
export default useDebouncedValue; |
@@ -8,2 +8,3 @@ "use strict"; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const defaultIsEqual = (a, b) => a === b; | ||
/** | ||
@@ -15,11 +16,16 @@ * Debounce a value change by a specified number of milliseconds. Useful | ||
* @param value | ||
* @param delayMs | ||
* @param waitOrOptions | ||
* @returns | ||
*/ | ||
function useDebouncedValue(value, delayMs = 500) { | ||
const [debouncedValue, setDebouncedValue] = (0, _useDebouncedState.default)(value, delayMs); | ||
function useDebouncedValue(value, waitOrOptions = 500) { | ||
const previousValueRef = (0, _react.useRef)(value); | ||
const isEqual = typeof waitOrOptions === 'object' ? waitOrOptions.isEqual || defaultIsEqual : defaultIsEqual; | ||
const [debouncedValue, setDebouncedValue] = (0, _useDebouncedState.default)(value, waitOrOptions); | ||
(0, _react.useDebugValue)(debouncedValue); | ||
(0, _react.useEffect)(() => { | ||
setDebouncedValue(value); | ||
}, [value, delayMs]); | ||
if (!isEqual || !isEqual(previousValueRef.current, value)) { | ||
previousValueRef.current = value; | ||
setDebouncedValue(value); | ||
} | ||
}); | ||
return debouncedValue; | ||
@@ -26,0 +32,0 @@ } |
@@ -0,1 +1,2 @@ | ||
import { MutableRefObject } from 'react'; | ||
/** | ||
@@ -22,2 +23,3 @@ * Returns a controller object for setting a timeout that is properly cleaned up | ||
clear: () => void; | ||
handleRef: MutableRefObject<any>; | ||
}; |
@@ -61,5 +61,6 @@ "use strict"; | ||
set, | ||
clear | ||
clear, | ||
handleRef | ||
}; | ||
}, []); | ||
} |
@@ -0,8 +1,14 @@ | ||
export interface UseDebouncedCallbackOptions { | ||
wait: number; | ||
leading?: boolean; | ||
trailing?: boolean; | ||
maxWait?: number; | ||
} | ||
/** | ||
* Creates a debounced function that will invoke the input function after the | ||
* specified delay. | ||
* specified wait. | ||
* | ||
* @param fn a function that will be debounced | ||
* @param delay The milliseconds delay before invoking the function | ||
* @param waitOrOptions a wait in milliseconds or a debounce configuration | ||
*/ | ||
export default function useDebouncedCallback<TCallback extends (...args: any[]) => any>(fn: TCallback, delay: number): (...args: Parameters<TCallback>) => void; | ||
export default function useDebouncedCallback<TCallback extends (...args: any[]) => any>(fn: TCallback, waitOrOptions: number | UseDebouncedCallbackOptions): (...args: Parameters<TCallback>) => void; |
@@ -1,18 +0,101 @@ | ||
import { useCallback } from 'react'; | ||
import { useMemo, useRef } from 'react'; | ||
import useTimeout from './useTimeout'; | ||
/** | ||
* Creates a debounced function that will invoke the input function after the | ||
* specified delay. | ||
* specified wait. | ||
* | ||
* @param fn a function that will be debounced | ||
* @param delay The milliseconds delay before invoking the function | ||
* @param waitOrOptions a wait in milliseconds or a debounce configuration | ||
*/ | ||
export default function useDebouncedCallback(fn, delay) { | ||
export default function useDebouncedCallback(fn, waitOrOptions) { | ||
const lastCallTimeRef = useRef(null); | ||
const lastInvokeTimeRef = useRef(0); | ||
const isTimerSetRef = useRef(false); | ||
const lastArgsRef = useRef(null); | ||
const { | ||
wait, | ||
maxWait, | ||
leading = false, | ||
trailing = true | ||
} = typeof waitOrOptions === 'number' ? { | ||
wait: waitOrOptions | ||
} : waitOrOptions; | ||
const timeout = useTimeout(); | ||
return useCallback((...args) => { | ||
timeout.set(() => { | ||
fn(...args); | ||
}, delay); | ||
}, [fn, delay]); | ||
return useMemo(() => { | ||
const hasMaxWait = !!maxWait; | ||
function leadingEdge(time) { | ||
// Reset any `maxWait` timer. | ||
lastInvokeTimeRef.current = time; | ||
// Start the timer for the trailing edge. | ||
isTimerSetRef.current = true; | ||
timeout.set(timerExpired, wait); | ||
// Invoke the leading edge. | ||
if (leading) { | ||
invokeFunc(time); | ||
} | ||
} | ||
function trailingEdge(time) { | ||
isTimerSetRef.current = false; | ||
// Only invoke if we have `lastArgs` which means `func` has been | ||
// debounced at least once. | ||
if (trailing && lastArgsRef.current) { | ||
return invokeFunc(time); | ||
} | ||
lastArgsRef.current = null; | ||
} | ||
function timerExpired() { | ||
var _lastCallTimeRef$curr; | ||
var time = Date.now(); | ||
if (shouldInvoke(time)) { | ||
return trailingEdge(time); | ||
} | ||
const timeSinceLastCall = time - ((_lastCallTimeRef$curr = lastCallTimeRef.current) != null ? _lastCallTimeRef$curr : 0); | ||
const timeSinceLastInvoke = time - lastInvokeTimeRef.current; | ||
const timeWaiting = wait - timeSinceLastCall; | ||
// Restart the timer. | ||
timeout.set(timerExpired, hasMaxWait ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting); | ||
} | ||
function invokeFunc(time) { | ||
var _lastArgsRef$current; | ||
const args = (_lastArgsRef$current = lastArgsRef.current) != null ? _lastArgsRef$current : []; | ||
lastArgsRef.current = null; | ||
lastInvokeTimeRef.current = time; | ||
return fn(...args); | ||
} | ||
function shouldInvoke(time) { | ||
var _lastCallTimeRef$curr2; | ||
const timeSinceLastCall = time - ((_lastCallTimeRef$curr2 = lastCallTimeRef.current) != null ? _lastCallTimeRef$curr2 : 0); | ||
const timeSinceLastInvoke = time - lastInvokeTimeRef.current; | ||
// Either this is the first call, activity has stopped and we're at the | ||
// trailing edge, the system time has gone backwards and we're treating | ||
// it as the trailing edge, or we've hit the `maxWait` limit. | ||
return lastCallTimeRef.current === null || timeSinceLastCall >= wait || timeSinceLastCall < 0 || hasMaxWait && timeSinceLastInvoke >= maxWait; | ||
} | ||
return (...args) => { | ||
const time = Date.now(); | ||
const isInvoking = shouldInvoke(time); | ||
lastArgsRef.current = args; | ||
lastCallTimeRef.current = time; | ||
if (isInvoking) { | ||
if (!isTimerSetRef.current) { | ||
return leadingEdge(lastCallTimeRef.current); | ||
} | ||
if (hasMaxWait) { | ||
// Handle invocations in a tight loop. | ||
isTimerSetRef.current = true; | ||
setTimeout(timerExpired, wait); | ||
return invokeFunc(lastCallTimeRef.current); | ||
} | ||
} | ||
if (!isTimerSetRef.current) { | ||
isTimerSetRef.current = true; | ||
setTimeout(timerExpired, wait); | ||
} | ||
}; | ||
}, [fn, wait, maxWait, leading, trailing]); | ||
} |
import { Dispatch, SetStateAction } from 'react'; | ||
import { UseDebouncedCallbackOptions } from './useDebouncedCallback'; | ||
/** | ||
@@ -13,4 +14,4 @@ * Similar to `useState`, except the setter function is debounced by | ||
* @param initialState initial state value | ||
* @param delay The milliseconds delay before a new value is set | ||
* @param delayOrOptions The milliseconds delay before a new value is set, or options object | ||
*/ | ||
export default function useDebouncedState<T>(initialState: T, delay: number): [T, Dispatch<SetStateAction<T>>]; | ||
export default function useDebouncedState<T>(initialState: T, delayOrOptions: number | UseDebouncedCallbackOptions): [T, Dispatch<SetStateAction<T>>]; |
@@ -15,8 +15,8 @@ import { useState } from 'react'; | ||
* @param initialState initial state value | ||
* @param delay The milliseconds delay before a new value is set | ||
* @param delayOrOptions The milliseconds delay before a new value is set, or options object | ||
*/ | ||
export default function useDebouncedState(initialState, delay) { | ||
export default function useDebouncedState(initialState, delayOrOptions) { | ||
const [state, setState] = useState(initialState); | ||
const debouncedSetState = useDebouncedCallback(setState, delay); | ||
const debouncedSetState = useDebouncedCallback(setState, delayOrOptions); | ||
return [state, debouncedSetState]; | ||
} |
@@ -0,1 +1,5 @@ | ||
import { UseDebouncedCallbackOptions } from './useDebouncedCallback'; | ||
export type UseDebouncedValueOptions = UseDebouncedCallbackOptions & { | ||
isEqual?: (a: any, b: any) => boolean; | ||
}; | ||
/** | ||
@@ -7,6 +11,6 @@ * Debounce a value change by a specified number of milliseconds. Useful | ||
* @param value | ||
* @param delayMs | ||
* @param waitOrOptions | ||
* @returns | ||
*/ | ||
declare function useDebouncedValue<TValue>(value: TValue, delayMs?: number): TValue; | ||
declare function useDebouncedValue<TValue>(value: TValue, waitOrOptions?: number | UseDebouncedValueOptions): TValue; | ||
export default useDebouncedValue; |
@@ -1,4 +0,4 @@ | ||
import { useEffect, useDebugValue } from 'react'; | ||
import { useEffect, useDebugValue, useRef } from 'react'; | ||
import useDebouncedState from './useDebouncedState'; | ||
const defaultIsEqual = (a, b) => a === b; | ||
/** | ||
@@ -10,13 +10,18 @@ * Debounce a value change by a specified number of milliseconds. Useful | ||
* @param value | ||
* @param delayMs | ||
* @param waitOrOptions | ||
* @returns | ||
*/ | ||
function useDebouncedValue(value, delayMs = 500) { | ||
const [debouncedValue, setDebouncedValue] = useDebouncedState(value, delayMs); | ||
function useDebouncedValue(value, waitOrOptions = 500) { | ||
const previousValueRef = useRef(value); | ||
const isEqual = typeof waitOrOptions === 'object' ? waitOrOptions.isEqual || defaultIsEqual : defaultIsEqual; | ||
const [debouncedValue, setDebouncedValue] = useDebouncedState(value, waitOrOptions); | ||
useDebugValue(debouncedValue); | ||
useEffect(() => { | ||
setDebouncedValue(value); | ||
}, [value, delayMs]); | ||
if (!isEqual || !isEqual(previousValueRef.current, value)) { | ||
previousValueRef.current = value; | ||
setDebouncedValue(value); | ||
} | ||
}); | ||
return debouncedValue; | ||
} | ||
export default useDebouncedValue; |
@@ -0,1 +1,2 @@ | ||
import { MutableRefObject } from 'react'; | ||
/** | ||
@@ -22,2 +23,3 @@ * Returns a controller object for setting a timeout that is properly cleaned up | ||
clear: () => void; | ||
handleRef: MutableRefObject<any>; | ||
}; |
@@ -57,5 +57,6 @@ import { useMemo, useRef } from 'react'; | ||
set, | ||
clear | ||
clear, | ||
handleRef | ||
}; | ||
}, []); | ||
} |
{ | ||
"name": "@restart/hooks", | ||
"version": "0.4.11", | ||
"version": "0.4.12", | ||
"main": "cjs/index.js", | ||
@@ -5,0 +5,0 @@ "types": "cjs/index.d.ts", |
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
192322
5381