From 7225c6a16878e169ebafe83dd2f7449d71bde21a Mon Sep 17 00:00:00 2001 From: wo-o29 Date: Fri, 12 Sep 2025 15:57:05 +0900 Subject: [PATCH 1/5] fix: make useDebouncedCallback generic for flexible onChange parameter type --- .../useDebouncedCallback/useDebouncedCallback.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/hooks/useDebouncedCallback/useDebouncedCallback.ts b/src/hooks/useDebouncedCallback/useDebouncedCallback.ts index 1ffa16b8..2263f3fd 100644 --- a/src/hooks/useDebouncedCallback/useDebouncedCallback.ts +++ b/src/hooks/useDebouncedCallback/useDebouncedCallback.ts @@ -30,17 +30,20 @@ type DebounceOptions = { * return debouncedSetQuery(e.target.value)} />; * } */ -export function useDebouncedCallback({ +export function useDebouncedCallback({ onChange, timeThreshold, leading = false, trailing = true, }: DebounceOptions & { - onChange: (newValue: boolean) => void; + onChange: (newValue: T) => void; timeThreshold: number; }) { const handleChange = usePreservedCallback(onChange); - const ref = useRef({ value: false, clearPreviousDebounce: () => {} }); + const ref = useRef<{ value: T | null; clearPreviousDebounce: () => void }>({ + value: null, + clearPreviousDebounce: () => {}, + }); useEffect(() => { const current = ref.current; @@ -63,7 +66,7 @@ export function useDebouncedCallback({ }, [leading, trailing]); return useCallback( - (nextValue: boolean) => { + (nextValue: T) => { if (nextValue === ref.current.value) { return; } From 952ec41994a974961bfcbcf84d2a1e15d12c6272 Mon Sep 17 00:00:00 2001 From: wo-o29 Date: Fri, 12 Sep 2025 16:12:38 +0900 Subject: [PATCH 2/5] fix: update onChange parameter type in useDebouncedCallback documentation --- src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md | 2 +- src/hooks/useDebouncedCallback/useDebouncedCallback.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md b/src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md index 5c5dff08..c512191b 100644 --- a/src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md +++ b/src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md @@ -18,7 +18,7 @@ function useDebouncedCallback(options: Object): Function; :nested="[ { name: 'options.onChange', - type: 'Function', + type: '(newValue: T) => void', required: true, description: '디바운스할 콜백 함수예요.', }, diff --git a/src/hooks/useDebouncedCallback/useDebouncedCallback.md b/src/hooks/useDebouncedCallback/useDebouncedCallback.md index 362ba82d..c017e150 100644 --- a/src/hooks/useDebouncedCallback/useDebouncedCallback.md +++ b/src/hooks/useDebouncedCallback/useDebouncedCallback.md @@ -18,7 +18,7 @@ function useDebouncedCallback(options: Object): Function; :nested="[ { name: 'options.onChange', - type: 'Function', + type: '(newValue: T) => void', required: true, description: 'The callback function to debounce.', }, From ab03cd546653c96171d04e5ff2d9e8839ad21223 Mon Sep 17 00:00:00 2001 From: wo-o29 Date: Fri, 12 Sep 2025 16:24:47 +0900 Subject: [PATCH 3/5] test: optimize debounce timing in useDebouncedCallback tests --- .../useDebouncedCallback/useDebouncedCallback.spec.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/hooks/useDebouncedCallback/useDebouncedCallback.spec.ts b/src/hooks/useDebouncedCallback/useDebouncedCallback.spec.ts index ae610bea..c6485411 100644 --- a/src/hooks/useDebouncedCallback/useDebouncedCallback.spec.ts +++ b/src/hooks/useDebouncedCallback/useDebouncedCallback.spec.ts @@ -24,20 +24,15 @@ describe('useDebouncedCallback', () => { expect(onChange).not.toBeCalled(); result.current(true); - vi.advanceTimersByTime(50); - expect(onChange).not.toBeCalled(); - - result.current(false); - vi.advanceTimersByTime(50); + vi.advanceTimersByTime(100); expect(onChange).toBeCalledTimes(1); expect(onChange).toBeCalledWith(true); result.current(false); - vi.advanceTimersByTime(50); + vi.advanceTimersByTime(99); expect(onChange).toBeCalledTimes(1); - expect(onChange).toBeCalledWith(true); - vi.advanceTimersByTime(50); + vi.advanceTimersByTime(1); expect(onChange).toBeCalledTimes(2); expect(onChange).toBeCalledWith(false); }); From 7f7ece9e7fa39037e58e3405e58e64423a0ee586 Mon Sep 17 00:00:00 2001 From: wo-o29 Date: Sun, 14 Sep 2025 14:40:20 +0900 Subject: [PATCH 4/5] fix: update test cases in useDebouncedCallback to use correct values --- .../useDebouncedCallback.spec.ts | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/hooks/useDebouncedCallback/useDebouncedCallback.spec.ts b/src/hooks/useDebouncedCallback/useDebouncedCallback.spec.ts index c6485411..4c27cfff 100644 --- a/src/hooks/useDebouncedCallback/useDebouncedCallback.spec.ts +++ b/src/hooks/useDebouncedCallback/useDebouncedCallback.spec.ts @@ -20,21 +20,26 @@ describe('useDebouncedCallback', () => { const onChange = vi.fn(); const { result } = renderHookSSR(() => useDebouncedCallback({ onChange, timeThreshold: 100 })); - result.current(true); + result.current('hi'); expect(onChange).not.toBeCalled(); - result.current(true); - vi.advanceTimersByTime(100); + result.current('hi'); + vi.advanceTimersByTime(50); + expect(onChange).not.toBeCalled(); + + result.current(null); + vi.advanceTimersByTime(50); expect(onChange).toBeCalledTimes(1); - expect(onChange).toBeCalledWith(true); + expect(onChange).toBeCalledWith('hi'); - result.current(false); - vi.advanceTimersByTime(99); + result.current(null); + vi.advanceTimersByTime(50); expect(onChange).toBeCalledTimes(1); + expect(onChange).toBeCalledWith('hi'); - vi.advanceTimersByTime(1); + vi.advanceTimersByTime(50); expect(onChange).toBeCalledTimes(2); - expect(onChange).toBeCalledWith(false); + expect(onChange).toBeCalledWith(null); }); it('should handle leading edge', () => { From f2d0f7a1595268db3ed9839ea732a7be9152d853 Mon Sep 17 00:00:00 2001 From: wo-o29 Date: Sun, 14 Sep 2025 14:46:01 +0900 Subject: [PATCH 5/5] fix: update onChange parameter type in useDebouncedCallback documentation --- src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md | 2 +- src/hooks/useDebouncedCallback/useDebouncedCallback.md | 2 +- src/hooks/useDebouncedCallback/useDebouncedCallback.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md b/src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md index c512191b..13487de2 100644 --- a/src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md +++ b/src/hooks/useDebouncedCallback/ko/useDebouncedCallback.md @@ -52,7 +52,7 @@ function useDebouncedCallback(options: Object): Function; diff --git a/src/hooks/useDebouncedCallback/useDebouncedCallback.md b/src/hooks/useDebouncedCallback/useDebouncedCallback.md index c017e150..426703b8 100644 --- a/src/hooks/useDebouncedCallback/useDebouncedCallback.md +++ b/src/hooks/useDebouncedCallback/useDebouncedCallback.md @@ -52,7 +52,7 @@ function useDebouncedCallback(options: Object): Function; diff --git a/src/hooks/useDebouncedCallback/useDebouncedCallback.ts b/src/hooks/useDebouncedCallback/useDebouncedCallback.ts index 2263f3fd..0c858fc5 100644 --- a/src/hooks/useDebouncedCallback/useDebouncedCallback.ts +++ b/src/hooks/useDebouncedCallback/useDebouncedCallback.ts @@ -16,12 +16,12 @@ type DebounceOptions = { * Note that if both 'leading' and 'trailing' are set, the function will be called at both the start and end of the delay period. However, it must be called at least twice within debounceMs interval for this to happen, since one debounced function call cannot trigger the function twice. * * @param {Object} options - The options object. - * @param {Function} options.onChange - The callback function to debounce. + * @param {(newValue: T) => void} options.onChange - The callback function to debounce. * @param {number} options.timeThreshold - The number of milliseconds to delay the function execution. * @param {boolean} [options.leading=false] - If `true`, the function is called at the start of the sequence. * @param {boolean} [options.trailing=true] - If `true`, the function is called at the end of the sequence. * - * @returns {Function} A debounced function that delays invoking the callback. + * @returns {(newValue: T) => void} A debounced function that delays invoking the callback. * * @example * function SearchInput() {