From 62d25cb1e684eb26e4b92a22455a5623354d9c90 Mon Sep 17 00:00:00 2001 From: AvrBat <149933078+avrbar@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:06:12 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=90=9B=20Fix=20bug=20in=20useDebounce?= =?UTF-8?q?dCallback=20doesn't=20consider=20its=20dependencies=20(#420)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 Fix bug in useDebouncedCallback where dependencies were not considered (#420) --- src/useDebouncedCallback.ts | 6 +++--- test/useDebouncedCallback.spec.js | 33 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/useDebouncedCallback.ts b/src/useDebouncedCallback.ts index 3a613913..ded628c4 100644 --- a/src/useDebouncedCallback.ts +++ b/src/useDebouncedCallback.ts @@ -20,18 +20,18 @@ const defaultOptions: DebounceOptions = { * If time is not defined, its default value will be 250ms. */ const useDebouncedCallback = - (fn: TCallback, dependencies?: DependencyList, wait: number = 600, options: DebounceOptions = defaultOptions) => { + (fn: TCallback, dependencies: DependencyList = [], wait: number = 600, options: DebounceOptions = defaultOptions) => { const debounced = useRef(debounce(fn, wait, options)) useEffect(() => { debounced.current = debounce(fn, wait, options) - }, [fn, wait, options]) + }, [fn, wait, options, ...dependencies]) useWillUnmount(() => { debounced.current?.cancel() }) - return useCallback(debounced.current, dependencies ?? []) + return useCallback((...args: Parameters) => debounced.current(...args), [...dependencies]) } export default useDebouncedCallback diff --git a/test/useDebouncedCallback.spec.js b/test/useDebouncedCallback.spec.js index 4e4eb084..ac1f69dd 100644 --- a/test/useDebouncedCallback.spec.js +++ b/test/useDebouncedCallback.spec.js @@ -47,4 +47,37 @@ describe('useDebouncedCallback', () => { expect(spy.called).to.be.true expect(spy.callCount).to.equal(1) }) + + it('should use the latest callback', async () => { + const firstSpy = sinon.spy(); + const secondSpy = sinon.spy(); + + const TestComponent = () => { + const [callback, setCallback] = React.useState(() => firstSpy); + const debouncedCallback = useDebouncedCallback(callback, [callback], 250); + + React.useEffect(() => { + debouncedCallback(); + debouncedCallback(); + + setTimeout(() => { + setCallback(() => secondSpy); + }, 100); + + setTimeout(() => { + debouncedCallback(); + debouncedCallback(); + }, 200); + }, [debouncedCallback]); + + return
; + }; + + render(); + + await promiseDelay(600); + + expect(firstSpy.callCount).to.equal(1); + expect(secondSpy.callCount).to.equal(1); + }) }) From 0f436de111ab29ff18aa7cabdaa898da513e1e91 Mon Sep 17 00:00:00 2001 From: AvrBat <149933078+avrbar@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:03:01 +0300 Subject: [PATCH 2/3] Update package.json Update Version from 5.0.2 to 5.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 116f9c0f..45348f21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "beautiful-react-hooks", - "version": "5.0.2", + "version": "5.0.3", "description": "A collection of beautiful (and hopefully useful) React hooks to speed-up your components and hooks development", "main": "index.js", "module": "esm/index.js", From 179ec6f3392c5b75666f4dcbc3113f120aeb1946 Mon Sep 17 00:00:00 2001 From: AvrBat Date: Mon, 2 Dec 2024 13:14:14 +0200 Subject: [PATCH 3/3] Fix Bug In useThrottledCallback Doesn't Consider Its Dependencies --- src/useThrottledCallback.ts | 6 +++--- test/useThrottledCallback.spec.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/useThrottledCallback.ts b/src/useThrottledCallback.ts index 8856ca38..177926e3 100644 --- a/src/useThrottledCallback.ts +++ b/src/useThrottledCallback.ts @@ -19,18 +19,18 @@ const defaultOptions: ThrottleSettings = { * If time is not defined, its default value will be 250ms. */ const useThrottledCallback = - (fn: TCallback, dependencies?: DependencyList, wait: number = 600, options: ThrottleSettings = defaultOptions) => { + (fn: TCallback, dependencies: DependencyList = [], wait: number = 600, options: ThrottleSettings = defaultOptions) => { const throttled = useRef(throttle(fn, wait, options)) useEffect(() => { throttled.current = throttle(fn, wait, options) - }, [fn, wait, options]) + }, [fn, wait, options, ...dependencies]) useWillUnmount(() => { throttled.current?.cancel() }) - return useCallback(throttled.current, dependencies ?? []) + return useCallback((...args: Parameters) => throttled.current(...args), [...dependencies]) } export default useThrottledCallback diff --git a/test/useThrottledCallback.spec.js b/test/useThrottledCallback.spec.js index 4f89d340..09425ff4 100644 --- a/test/useThrottledCallback.spec.js +++ b/test/useThrottledCallback.spec.js @@ -47,4 +47,34 @@ describe('useThrottledCallback', () => { expect(spy.called).to.be.true expect(spy.callCount).to.equal(1) }) + + it('should use the latest callback', async () => { + const firstSpy = sinon.spy(); + const secondSpy = sinon.spy(); + + const TestComponent = () => { + const [callback, setCallback] = React.useState(() => firstSpy); + const throttledCallback = useThrottledCallback(callback, [callback], 250); + + React.useEffect(() => { + throttledCallback(); + throttledCallback(); + }, [throttledCallback]); + + React.useEffect(() => { + setTimeout(() => { + setCallback(() => secondSpy); + }, 100); + }, []); + + return
; + }; + + render(); + + await promiseDelay(600); + + expect(firstSpy.callCount).to.equal(1); + expect(secondSpy.callCount).to.equal(1); + }) })