Skip to content

Commit

Permalink
🏀 Add useDebounce hook
Browse files Browse the repository at this point in the history
  • Loading branch information
ClumsyVlad committed May 8, 2024
1 parent d27c882 commit 2f54c10
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 0 deletions.
Binary file added bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@
"@rainbow-me/rainbowkit": "^2.0.5",
"@t3-oss/env-nextjs": "^0.9.2",
"@tanstack/react-query": "^5.29.2",
"@types/lodash.debounce": "^4.0.9",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"csv": "^6.3.8",
"drizzle-orm": "^0.30.9",
"lexical": "^0.13.1",
"lodash.debounce": "^4.0.8",
"next": "14.1.4",
"next-axiom": "^1.1.1",
"postgres": "^3.4.4",
Expand Down
63 changes: 63 additions & 0 deletions src/hooks/useDebounceCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useEffect, useMemo, useRef } from "react";
import debounce from "lodash.debounce";

import { useUnmount } from "./useUnmounts";

type DebounceOptions = {
leading?: boolean;
trailing?: boolean;
maxWait?: number;
};

type ControlFunctions = {
cancel: () => void;
flush: () => void;
isPending: () => boolean;
};

export type DebouncedState<T extends (...args: any) => ReturnType<T>> = ((
...args: Parameters<T>
) => ReturnType<T> | undefined) &
ControlFunctions;

export function useDebounceCallback<T extends (...args: any) => ReturnType<T>>(
func: T,
delay = 500,
options?: DebounceOptions,
): DebouncedState<T> {
const debouncedFunc = useRef<ReturnType<typeof debounce>>();

useUnmount(() => {
if (debouncedFunc.current) {
debouncedFunc.current.cancel();
}
});

const debounced = useMemo(() => {
const debouncedFuncInstance = debounce(func, delay, options);

const wrappedFunc: DebouncedState<T> = (...args: Parameters<T>) => {
return debouncedFuncInstance(...args);
};

wrappedFunc.cancel = () => {
debouncedFuncInstance.cancel();
};

wrappedFunc.isPending = () => {
return !!debouncedFunc.current;
};

wrappedFunc.flush = () => {
return debouncedFuncInstance.flush();
};

return wrappedFunc;
}, [func, delay, options]);

useEffect(() => {
debouncedFunc.current = debounce(func, delay, options);
}, [func, delay, options]);

return debounced;
}
14 changes: 14 additions & 0 deletions src/hooks/useUnmounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useEffect, useRef } from "react";

export function useUnmount(func: () => void) {
const funcRef = useRef(func);

funcRef.current = func;

useEffect(
() => () => {
funcRef.current();
},
[],
);
}

0 comments on commit 2f54c10

Please sign in to comment.