From 0a9d11c095ab3b9e1926af4c8a0cd2181af5609f Mon Sep 17 00:00:00 2001 From: Ashish-simpleCoder Date: Sat, 11 Oct 2025 11:43:59 +0530 Subject: [PATCH] fix: cleanup of timeout/interval effect on interval-prop change --- .changeset/silver-radios-tickle.md | 5 +++++ src/lib/use-interval-effect/index.test.tsx | 22 ++++++++++++++++++++++ src/lib/use-interval-effect/index.tsx | 4 ++-- src/lib/use-timeout-effect/index.test.tsx | 19 +++++++++++++++++++ src/lib/use-timeout-effect/index.tsx | 4 ++-- 5 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 .changeset/silver-radios-tickle.md diff --git a/.changeset/silver-radios-tickle.md b/.changeset/silver-radios-tickle.md new file mode 100644 index 0000000..a9ac39e --- /dev/null +++ b/.changeset/silver-radios-tickle.md @@ -0,0 +1,5 @@ +--- +'classic-react-hooks': minor +--- + +- Fix timeout and interval effect cleanup when interval changes diff --git a/src/lib/use-interval-effect/index.test.tsx b/src/lib/use-interval-effect/index.test.tsx index 2fe4ed1..1707871 100644 --- a/src/lib/use-interval-effect/index.test.tsx +++ b/src/lib/use-interval-effect/index.test.tsx @@ -69,4 +69,26 @@ describe('use-interval-effect', () => { unmount() expect(fn).toHaveBeenCalledTimes(0) }) + + it('should clear the interval when prop changes', () => { + const fn = vi.fn() + + let interval = 300 + const { result, rerender } = renderHook(() => useIntervalEffect({ handler: fn, interval })) + + vi.advanceTimersByTime(200) + expect(fn).not.toHaveBeenCalled() + + interval = 500 + rerender() + + vi.advanceTimersByTime(200) + expect(fn).not.toHaveBeenCalled() + + vi.advanceTimersByTime(300) + expect(fn).toHaveBeenCalledTimes(1) + + vi.advanceTimersByTime(500) + expect(fn).toHaveBeenCalledTimes(2) + }) }) diff --git a/src/lib/use-interval-effect/index.tsx b/src/lib/use-interval-effect/index.tsx index 7e4af32..4224563 100644 --- a/src/lib/use-interval-effect/index.tsx +++ b/src/lib/use-interval-effect/index.tsx @@ -47,9 +47,9 @@ export default function useIntervalEffect({ handler, interval = 100 }: { handler }) useEffect(() => { - intervalId.current = setInterval(() => paramsRef.current.handler(), paramsRef.current.interval) + intervalId.current = setInterval(() => paramsRef.current.handler(), interval) return handlers.current.clearTimer - }, []) + }, [interval]) return { clearTimer: handlers.current.clearTimer, diff --git a/src/lib/use-timeout-effect/index.test.tsx b/src/lib/use-timeout-effect/index.test.tsx index b153c24..8c36a6d 100644 --- a/src/lib/use-timeout-effect/index.test.tsx +++ b/src/lib/use-timeout-effect/index.test.tsx @@ -49,6 +49,25 @@ describe('useTimeoutEffect', () => { }) describe('clearTimer functionality', () => { + it('should clear the timeout when prop changes', () => { + const fn = vi.fn() + + let timeout = 300 + const { result, rerender } = renderHook(() => useTimeoutEffect({ handler: fn, timeout })) + + vi.advanceTimersByTime(200) + expect(fn).not.toHaveBeenCalled() + + timeout = 500 + rerender() + + vi.advanceTimersByTime(200) + expect(fn).not.toHaveBeenCalled() + + vi.advanceTimersByTime(300) + expect(fn).toHaveBeenCalledTimes(1) + }) + it('should clear the timeout with clearTimer before timeout expires', () => { const fn = vi.fn() const { result } = renderHook(() => useTimeoutEffect({ handler: fn, timeout: 500 })) diff --git a/src/lib/use-timeout-effect/index.tsx b/src/lib/use-timeout-effect/index.tsx index fc5e0cf..536ac20 100644 --- a/src/lib/use-timeout-effect/index.tsx +++ b/src/lib/use-timeout-effect/index.tsx @@ -41,9 +41,9 @@ export default function useTimeoutEffect({ handler, timeout = 100 }: { handler: }) useEffect(() => { - timeoutId.current = setTimeout(() => paramsRef.current.handler(), paramsRef.current.timeout) + timeoutId.current = setTimeout(() => paramsRef.current.handler(), timeout) return handlers.current.clearTimer - }, []) + }, [timeout]) return { clearTimer: handlers.current.clearTimer,