Skip to content

Commit 9cb92fa

Browse files
committed
fix: prevent memory leak in debounced submit function
- Add cancel method to debounce utility function with proper TypeScript typing - Add cleanup effect in useStacSearch to cancel pending debounced calls - Prevent orphaned timeouts when component unmounts or _submit function changes Previously, when _submit changed, a new debounced function was created but the old one's timeout wasn't cancelled, causing potential memory leaks and race conditions where stale searches could execute after newer ones.
1 parent ec62ab4 commit 9cb92fa

File tree

2 files changed

+23
-3
lines changed

2 files changed

+23
-3
lines changed

src/hooks/useStacSearch.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,14 @@ function useStacSearch(): StacSearchHook {
209209
setCurrentRequest({ type: 'search', payload });
210210
}, [getSearchPayload]);
211211

212-
const submit = useMemo(() => debounce(_submit), [_submit]);
212+
const submit = useMemo(() => debounce(_submit, 300), [_submit]);
213+
214+
// Clean up debounced function on unmount or when _submit changes
215+
useEffect(() => {
216+
return () => {
217+
submit.cancel();
218+
};
219+
}, [submit]);
213220

214221
return {
215222
submit,

src/utils/debounce.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
const debounce = <F extends (...args: any) => any>(fn: F, ms = 250) => {
2+
3+
type DebouncedFunction<T extends (...args: any) => any> = T & {
4+
cancel: () => void;
5+
};
6+
7+
const debounce = <F extends (...args: any) => any>(fn: F, ms = 250): DebouncedFunction<F> => {
38
let timeoutId: ReturnType<typeof setTimeout>;
49

5-
return function (this: any, ...args: any[]) {
10+
const debouncedFn = function (this: any, ...args: any[]) {
611
clearTimeout(timeoutId);
712
timeoutId = setTimeout(() => fn.apply(this, args), ms);
13+
} as F;
14+
15+
// Add cancel method to clear pending timeouts
16+
(debouncedFn as DebouncedFunction<F>).cancel = () => {
17+
clearTimeout(timeoutId);
818
};
19+
20+
return debouncedFn as DebouncedFunction<F>;
921
};
1022

1123
export default debounce;
24+
export type { DebouncedFunction };

0 commit comments

Comments
 (0)