Skip to content

Commit

Permalink
add debounce
Browse files Browse the repository at this point in the history
  • Loading branch information
krfong916 committed Nov 15, 2021
1 parent e1490bc commit c7fab08
Showing 1 changed file with 67 additions and 2 deletions.
69 changes: 67 additions & 2 deletions proof-of-concepts/usedebounce/src/hooks/useDebouncedCallback.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useRef } from 'react';

const noop = () => {};

export type UseDebounceOptions = {
leading?: boolean;
trailing?: boolean;
Expand All @@ -21,8 +23,14 @@ export interface DebouncedReturnFunction<
export default function useDebouncedCallback<
T extends (...args: any[]) => ReturnType<T>
>(fn: T, delay: number, options: UseDebounceOptions) {
const fnRef = useRef();
const { leading = false, trailing = true } = options;
const fnRef = useRef(fn);
const delayRef = useRef(delay);
const resultRef = useRef<ReturnType<T>>();
const startTimeRef = useRef();
const timerIDRef = useRef<NodeJS.Timeout>();
const isLeading = useRef(leading);
const isTrailing = useRef(trailing);

if (typeof fn !== 'function') {
throw new TypeError(
Expand All @@ -34,14 +42,71 @@ export default function useDebouncedCallback<
* Re-computes the memoized value when one of the dependencies have changed
*/
const debounce = React.useMemo(() => {
const timersDontExist = () => !timerIDRef.current && !startTimeRef.current;

const clearTimer = () => {
startTimeRef.current = undefined;
timerIDRef.current = undefined;
};

const resetTimer = () => {
if (timerIDRef.current) {
clearTimeout(timerIDRef.current);
setTimeout(() => {}, delayRef.current);
}
};

const shouldInvoke = (currentTime: number) => {
if (timersDontExist()) {
return true;
} else if (startTimeRef.current) {
const elapsed = currentTime - startTimeRef.current;
if (elapsed >= delayRef.current) return true;
}
return false;
};

// run the fn the user specified
// based on the arguments
const func: DebouncedReturnFunction<T> = (
...args: Parameters<T>
): ReturnType<T> => {};
): ReturnType<T> | undefined => {
const currentTime = Date.now();
const invocable = shouldInvoke(currentTime);
let scheduledFn = noop;

if (invocable) {
if (isLeading) fnRef.current(...args);

if (isTrailing) scheduledFn = () => fnRef.current(...args);

timerIDRef.current = setTimeout(() => {
scheduledFn();
clearTimer();
}, delayRef.current);
} else {
resetTimer();
}
return resultRef.current;
};
return func;
}, []);

// debounce is a function that encapsulates the memo call it will accepts a variable amount of arguments
return debounce;
}

// invocable means
// either time has passed and we can invoke
// invoke, then reset and start timer
// or the option is leading
// invoke, then set and start the timer
// trailing
// set and start the timer, invoke after
// if it's not invocable does that mean that we run the timer
// what happens if it is not invocable?
// do we have a timeout currently running?
// if so, reset the timeout
// if not, then start the timeout
// and assign the id
// this sounds the same

0 comments on commit c7fab08

Please sign in to comment.