From 989d6c0efb4eef080ed78330233186d7b0c249e3 Mon Sep 17 00:00:00 2001 From: Nikita Mostovoy Date: Sat, 9 Mar 2019 09:45:24 +0300 Subject: [PATCH] add callPending callback to useDebouncedCallback method --- CHANGELOG.md | 4 +++ src/callback.d.ts | 2 +- src/callback.js | 14 ++++++++- test/callback.test.js | 71 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 984ccdd..16734b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.0 + +- add `callPending` callback to `useDebouncedCallback` method. + ## 1.0.0 The example with all features you can see here: https://codesandbox.io/s/4wvmp1xlw4 diff --git a/src/callback.d.ts b/src/callback.d.ts index 51e5eef..e69d58b 100644 --- a/src/callback.d.ts +++ b/src/callback.d.ts @@ -3,4 +3,4 @@ export default function useDebouncedCallback any>( delay: Number, deps: ReadonlyArray, options?: { maxWait?: number } -): [T, () => void]; +): [T, () => void, () => void]; diff --git a/src/callback.js b/src/callback.js index 4061712..73d61b6 100644 --- a/src/callback.js +++ b/src/callback.js @@ -13,6 +13,7 @@ export default function useDebouncedCallback(callback, delay, deps, options = {} clearTimeout(maxWaitHandler.current); maxWaitHandler.current = null; maxWaitArgs.current = []; + functionTimeoutHandler.current = null; }, [functionTimeoutHandler.current, maxWaitHandler.current]); useEffect( @@ -39,5 +40,16 @@ export default function useDebouncedCallback(callback, delay, deps, options = {} } }; - return [debouncedCallback, cancelDebouncedCallback]; + const callPending = () => { + // Call pending callback only if we have anything in our queue + if (!functionTimeoutHandler.current) { + return; + } + + debouncedFunction(...maxWaitArgs.current); + cancelDebouncedCallback(); + }; + + // For the moment, we use 3 args array so that we save backward compatibility + return [debouncedCallback, cancelDebouncedCallback, callPending]; } diff --git a/test/callback.test.js b/test/callback.test.js index b52ecef..ec49cb9 100644 --- a/test/callback.test.js +++ b/test/callback.test.js @@ -192,4 +192,75 @@ describe('useDebouncedCallback', () => { expect(callback.mock.calls.length).toBe(0); }); + + it('will call pending callback if callPending function is called', () => { + const callback = jest.fn(); + + function Component({ text }) { + const [debouncedCallback, , callPending] = useDebouncedCallback(callback, 500, [], { maxWait: 600 }); + debouncedCallback(); + if (text === 'test') { + callPending(); + } + return {text}; + } + const tree = Enzyme.mount(); + + expect(callback.mock.calls.length).toBe(0); + expect(tree.text()).toBe('one'); + + act(() => { + tree.setProps({ text: 'test' }); + }); + + expect(callback.mock.calls.length).toBe(1); + }); + + it('won\t call pending callback if callPending function is called and there are no items in queue', () => { + const callback = jest.fn(); + + function Component({ text }) { + const [debouncedCallback, , callPending] = useDebouncedCallback(callback, 500, [], { maxWait: 600 }); + if (text === 'test') { + callPending(); + } + return {text}; + } + const tree = Enzyme.mount(); + + expect(callback.mock.calls.length).toBe(0); + expect(tree.text()).toBe('one'); + + act(() => { + tree.setProps({ text: 'test' }); + }); + + expect(callback.mock.calls.length).toBe(0); + expect(tree.text()).toBe('test'); + }); + + it('won\t call pending callback if callPending function is called and cancel method is also executed', () => { + const callback = jest.fn(); + + function Component({ text }) { + const [debouncedCallback, cancel, callPending] = useDebouncedCallback(callback, 500, [], { maxWait: 600 }); + debouncedCallback(); + if (text === 'test') { + cancel(); + callPending(); + } + return {text}; + } + const tree = Enzyme.mount(); + + expect(callback.mock.calls.length).toBe(0); + expect(tree.text()).toBe('one'); + + act(() => { + tree.setProps({ text: 'test' }); + }); + + expect(callback.mock.calls.length).toBe(0); + expect(tree.text()).toBe('test'); + }); });