From 438a56606bb90d140425bd9f55e2b8c453f20151 Mon Sep 17 00:00:00 2001 From: xobotyi Date: Mon, 26 Aug 2019 09:58:28 +0300 Subject: [PATCH 1/2] make useTimeoutFn work properly: - reset function should clear previous timeout; - function change should not reset the timeout; --- docs/useTimeoutFn.md | 11 +++++++---- src/__tests__/useTimeoutFn.test.ts | 24 ++++++++++++++++++++---- src/useTimeoutFn.ts | 15 +++++++++++++-- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/docs/useTimeoutFn.md b/docs/useTimeoutFn.md index 52d259be..48e4bc2d 100644 --- a/docs/useTimeoutFn.md +++ b/docs/useTimeoutFn.md @@ -1,10 +1,13 @@ # `useTimeoutFn` -Calls given function after specified amount of milliseconds. -**Note:** this hook does not re-render component by itself. +Calls given function after specified amount of milliseconds. -Automatically cancels timeout on component unmount. -Automatically resets timeout on delay change. +Several thing about it's work: +- does not re-render component; +- automatically cancel timeout on cancel; +- automatically reset timeout on delay change; +- reset function call will cancel previous timeout; +- timeout will NOT be reset on function change. It will be called within the timeout, you have to reset it on your own when needed. ## Usage diff --git a/src/__tests__/useTimeoutFn.test.ts b/src/__tests__/useTimeoutFn.test.ts index 8164bcdc..c87739f5 100644 --- a/src/__tests__/useTimeoutFn.test.ts +++ b/src/__tests__/useTimeoutFn.test.ts @@ -28,9 +28,11 @@ describe('useTimeoutFn', () => { expect(typeof hook.result.current[2]).toBe('function'); }); - function getHook(ms: number = 5): [jest.Mock, RenderHookResult<{ delay: number }, UseTimeoutFnReturn>] { - const spy = jest.fn(); - return [spy, renderHook(({ delay = 5 }) => useTimeoutFn(spy, delay), { initialProps: { delay: ms } })]; + function getHook( + ms: number = 5, + fn: Function = jest.fn() + ): [Function, RenderHookResult<{ delay: number; cb: Function }, UseTimeoutFnReturn>] { + return [fn, renderHook(({ delay = 5, cb }) => useTimeoutFn(cb, delay), { initialProps: { delay: ms, cb: fn } })]; } it('should call passed function after given amount of time', () => { @@ -108,9 +110,23 @@ describe('useTimeoutFn', () => { const [spy, hook] = getHook(50); expect(spy).not.toHaveBeenCalled(); - hook.rerender({ delay: 5 }); + hook.rerender({ delay: 5, cb: spy }); jest.advanceTimersByTime(5); expect(spy).toHaveBeenCalledTimes(1); }); + + it('should NOT reset timeout on function change', () => { + const [spy, hook] = getHook(50); + + jest.advanceTimersByTime(25); + expect(spy).not.toHaveBeenCalled(); + + const spy2 = jest.fn(); + hook.rerender({ delay: 5, cb: spy2 }); + + jest.advanceTimersByTime(25); + expect(spy).not.toHaveBeenCalled(); + expect(spy2).toHaveBeenCalledTimes(1); + }); }); diff --git a/src/useTimeoutFn.ts b/src/useTimeoutFn.ts index 8bede34f..4f9e8c50 100644 --- a/src/useTimeoutFn.ts +++ b/src/useTimeoutFn.ts @@ -5,20 +5,31 @@ export type UseTimeoutFnReturn = [() => boolean | null, () => void, () => void]; export default function useTimeoutFn(fn: Function, ms: number = 0): UseTimeoutFnReturn { const ready = useRef(false); const timeout = useRef>(); + const callback = useRef(fn); const isReady = useCallback(() => ready.current, []); + const set = useCallback(() => { ready.current = false; + timeout.current && clearTimeout(timeout.current); + timeout.current = setTimeout(() => { ready.current = true; - fn(); + callback.current(); }, ms); - }, [ms, fn]); + }, [ms]); + const clear = useCallback(() => { ready.current = null; timeout.current && clearTimeout(timeout.current); }, []); + // update ref when function changes + useEffect(() => { + callback.current = fn; + }, [fn]); + + // set on mount, clear on unmount useEffect(() => { set(); From a6c2d65a3542836dbbb0f06ff4e2a444f8c33a7a Mon Sep 17 00:00:00 2001 From: xobotyi Date: Tue, 27 Aug 2019 00:01:46 +0300 Subject: [PATCH 2/2] fix typo; --- src/__tests__/useTimeoutFn.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/useTimeoutFn.test.ts b/src/__tests__/useTimeoutFn.test.ts index c87739f5..3c0b616f 100644 --- a/src/__tests__/useTimeoutFn.test.ts +++ b/src/__tests__/useTimeoutFn.test.ts @@ -123,7 +123,7 @@ describe('useTimeoutFn', () => { expect(spy).not.toHaveBeenCalled(); const spy2 = jest.fn(); - hook.rerender({ delay: 5, cb: spy2 }); + hook.rerender({ delay: 50, cb: spy2 }); jest.advanceTimersByTime(25); expect(spy).not.toHaveBeenCalled();