use-debounce
Advanced tools
Comparing version 0.0.9 to 1.0.0-beta
{ | ||
"name": "use-debounce", | ||
"version": "0.0.9", | ||
"version": "1.0.0-beta", | ||
"description": "Debounce hook for react", | ||
@@ -38,5 +38,5 @@ "main": "lib/index.js", | ||
"jest": "^23.6.0", | ||
"react": "16.8.2", | ||
"react-dom": "16.8.2" | ||
"react": "16.8.3", | ||
"react-dom": "16.8.3" | ||
} | ||
} |
@@ -32,3 +32,3 @@ # useDebounce react hook | ||
const [text, setText] = useState('Hello'); | ||
const debouncedText = useDebounce(text, 1000); | ||
const [value, cancelValue] = useDebounce(text, 1000); | ||
@@ -44,3 +44,3 @@ return ( | ||
<p>Actual value: {text}</p> | ||
<p>Debounce value: {debouncedText}</p> | ||
<p>Debounce value: {value}</p> | ||
</div> | ||
@@ -57,10 +57,10 @@ ); | ||
```js | ||
import useDebouncedCallback from 'use-debounce/callback'; | ||
function Input({ defaultValue }) { | ||
import useDebouncedCallback from 'use-debounce/lib/callback'; | ||
function Input({ defaultValue }) { | ||
const [value, setValue] = useState(defaultValue); | ||
// Debounce callback | ||
const debouncedFunction = useDebounce( | ||
const [debouncedCallback, cancelDebouncedCallback] = useDebouncedCallback( | ||
// function | ||
value => { | ||
(value) => { | ||
setValue(value); | ||
@@ -71,12 +71,9 @@ }, | ||
// deps (in case your function has closure dependency like https://reactjs.org/docs/hooks-reference.html#usecallback) | ||
[], | ||
[] | ||
); | ||
// you should use `e => debouncedFunction(e.target.value)` as react works with synthetic evens | ||
// you should use `e => debouncedCallback(e.target.value)` as react works with synthetic evens | ||
return ( | ||
<div> | ||
<input | ||
defaultValue={defaultValue} | ||
onChange={e => debouncedFunction(e.target.value)} | ||
/> | ||
<input defaultValue={defaultValue} onChange={(e) => debouncedCallback(e.target.value)} /> | ||
<p>Debounced value: {value}</p> | ||
@@ -99,3 +96,3 @@ </div> | ||
// Debounce callback | ||
const scrollHandler = useDebouncedCallback( | ||
const [scrollHandler] = useDebouncedCallback( | ||
// function | ||
@@ -102,0 +99,0 @@ () => { |
@@ -1,1 +0,1 @@ | ||
export default function useDebounce<T>(value: T, delay: number): T; | ||
export default function useDebounce<T>(value: T, delay: number, options?: { maxWait?: number }): [T, () => void)]; |
@@ -1,17 +0,21 @@ | ||
import { useState, useEffect } from 'react'; | ||
import { useState, useEffect, useRef, useCallback } from 'react'; | ||
export default function useDebounce(value, delay) { | ||
import useDebouncedCallback from './callback'; | ||
export default function useDebounce(value, delay, options = {}) { | ||
const [debouncedValue, setDebouncedValue] = useState(value); | ||
const [debouncedCallback, cancel] = useDebouncedCallback( | ||
(value) => setDebouncedValue(value), | ||
delay, | ||
[value], | ||
options | ||
); | ||
useEffect(() => { | ||
const handler = setTimeout(() => { | ||
setDebouncedValue(value); | ||
}, delay); | ||
if (debouncedValue !== value) { | ||
debouncedCallback(value); | ||
} | ||
}); | ||
return () => { | ||
clearTimeout(handler); | ||
}; | ||
}, [value, delay]); | ||
return debouncedValue; | ||
return [debouncedValue, cancel]; | ||
} |
export default function useDebouncedCallback<T extends (...args: any[]) => any>( | ||
callback: T, | ||
delay: Number, | ||
deps: ReadonlyArray<any> | ||
): T; | ||
deps: ReadonlyArray<any>, | ||
options?: { maxWait?: number } | ||
): [T, () => void]; |
import { useCallback, useEffect, useRef } from 'react'; | ||
export default function useDebouncedCallback(callback, delay, deps) { | ||
export default function useDebouncedCallback(callback, delay, deps, options = {}) { | ||
const { maxWait } = options; | ||
const maxWaitHandler = useRef(null); | ||
const maxWaitArgs = useRef([]); | ||
const functionTimeoutHandler = useRef(null); | ||
const debouncedFunction = useCallback(callback, deps); | ||
const cancelDebouncedCallback = useCallback(() => { | ||
clearTimeout(functionTimeoutHandler.current); | ||
clearTimeout(maxWaitHandler.current); | ||
maxWaitHandler.current = null; | ||
maxWaitArgs.current = []; | ||
}, [functionTimeoutHandler.current, maxWaitHandler.current]); | ||
useEffect( | ||
() => () => { | ||
clearTimeout(functionTimeoutHandler.current); | ||
cancelDebouncedCallback(); | ||
}, | ||
@@ -14,8 +25,20 @@ [] | ||
return (...args) => { | ||
const debouncedCallback = (...args) => { | ||
maxWaitArgs.current = args; | ||
clearTimeout(functionTimeoutHandler.current); | ||
functionTimeoutHandler.current = setTimeout(() => { | ||
debouncedFunction(...args); | ||
cancelDebouncedCallback(); | ||
}, delay); | ||
if (maxWait && !maxWaitHandler.current) { | ||
maxWaitHandler.current = setTimeout(() => { | ||
debouncedFunction(...maxWaitArgs.current); | ||
cancelDebouncedCallback(); | ||
}, maxWait); | ||
} | ||
}; | ||
return [debouncedCallback, cancelDebouncedCallback]; | ||
} |
@@ -11,4 +11,4 @@ import Enzyme from 'enzyme'; | ||
function Component() { | ||
const debouncedText = useDebounce('Hello world', 1000); | ||
return <div>{debouncedText}</div>; | ||
const [value] = useDebounce('Hello world', 1000); | ||
return <div>{value}</div>; | ||
} | ||
@@ -21,4 +21,4 @@ const tree = Enzyme.mount(<Component />); | ||
function Component({ text }) { | ||
const debouncedText = useDebounce(text, 1000); | ||
return <div>{debouncedText}</div>; | ||
const [value] = useDebounce(text, 1000); | ||
return <div>{value}</div>; | ||
} | ||
@@ -43,6 +43,30 @@ const tree = Enzyme.mount(<Component text={'Hello'} />); | ||
it('will cancel value when cancel method is called', () => { | ||
function Component({ text }) { | ||
const [value, cancelValue] = useDebounce(text, 1000); | ||
setTimeout(cancelValue, 500); | ||
return <div>{value}</div>; | ||
} | ||
const tree = Enzyme.mount(<Component text={'Hello'} />); | ||
// check inited value | ||
expect(tree.text()).toBe('Hello'); | ||
act(() => { | ||
tree.setProps({ text: 'Hello world' }); | ||
}); | ||
// timeout shouldn't have called yet | ||
expect(tree.text()).toBe('Hello'); | ||
act(() => { | ||
jest.runAllTimers(); | ||
}); | ||
// after runAllTimer text should not be updated as debounce was cancelled | ||
expect(tree.text()).toBe('Hello'); | ||
}); | ||
it('should apply the latest value', () => { | ||
function Component({ text }) { | ||
const debouncedText = useDebounce(text, 1000); | ||
return <div>{debouncedText}</div>; | ||
const [value] = useDebounce(text, 1000); | ||
return <div>{value}</div>; | ||
} | ||
@@ -69,2 +93,71 @@ const tree = Enzyme.mount(<Component text={'Hello'} />); | ||
}); | ||
it('should cancel maxWait callback', () => { | ||
function Component({ text }) { | ||
const [value, cancel] = useDebounce(text, 500, { maxWait: 600 }); | ||
if (text === 'Right value') { | ||
cancel(); | ||
} | ||
return <div>{value}</div>; | ||
} | ||
const tree = Enzyme.mount(<Component text={'Hello'} />); | ||
// check inited value | ||
expect(tree.text()).toBe('Hello'); | ||
act(() => { | ||
// this value shouldn't be applied, as we'll set up another one | ||
tree.setProps({ text: 'Wrong value' }); | ||
}); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
}); | ||
// timeout shouldn't have called yet | ||
expect(tree.text()).toBe('Hello'); | ||
act(() => { | ||
tree.setProps({ text: 'Right value' }); | ||
}); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
}); | ||
expect(tree.text()).toBe('Hello'); | ||
}); | ||
it('should apply the latest value if maxWait timeout is called', () => { | ||
function Component({ text }) { | ||
const [value] = useDebounce(text, 500, { maxWait: 600 }); | ||
return <div>{value}</div>; | ||
} | ||
const tree = Enzyme.mount(<Component text={'Hello'} />); | ||
// check inited value | ||
expect(tree.text()).toBe('Hello'); | ||
act(() => { | ||
// this value shouldn't be applied, as we'll set up another one | ||
tree.setProps({ text: 'Wrong value' }); | ||
}); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
}); | ||
// timeout shouldn't have called yet | ||
expect(tree.text()).toBe('Hello'); | ||
act(() => { | ||
tree.setProps({ text: 'Right value' }); | ||
}); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
}); | ||
// after runAllTimer text should be updated | ||
expect(tree.text()).toBe('Right value'); | ||
}); | ||
}); |
@@ -13,3 +13,3 @@ import Enzyme from 'enzyme'; | ||
function Component() { | ||
const debouncedCallback = useDebouncedCallback(callback, 1000, []); | ||
const [debouncedCallback] = useDebouncedCallback(callback, 1000, []); | ||
debouncedCallback(); | ||
@@ -35,3 +35,3 @@ return null; | ||
function Component() { | ||
const debouncedCallback = useDebouncedCallback(callback, 1000, []); | ||
const [debouncedCallback] = useDebouncedCallback(callback, 1000, []); | ||
debouncedCallback('Wrong param'); | ||
@@ -57,5 +57,23 @@ setTimeout(() => { | ||
it('will cancel delayed callback when cancel method is called', () => { | ||
const callback = jest.fn(); | ||
function Component() { | ||
const [debouncedCallback, cancelDebouncedCallback] = useDebouncedCallback(callback, 1000, []); | ||
debouncedCallback(); | ||
setTimeout(cancelDebouncedCallback, 500); | ||
return null; | ||
} | ||
Enzyme.mount(<Component />); | ||
act(() => { | ||
jest.runAllTimers(); | ||
}); | ||
expect(callback.mock.calls.length).toBe(0); | ||
}); | ||
it('will change callback function, if params from dependencies has changed', () => { | ||
function Component({ text }) { | ||
const debouncedCallback = useDebouncedCallback( | ||
const [debouncedCallback] = useDebouncedCallback( | ||
jest.fn(() => { | ||
@@ -82,3 +100,3 @@ expect(text).toBe('Right param'); | ||
function Component({ text }) { | ||
const debouncedCallback = useDebouncedCallback( | ||
const [debouncedCallback] = useDebouncedCallback( | ||
jest.fn(() => { | ||
@@ -102,2 +120,81 @@ expect(text).toBe('Right param'); | ||
}); | ||
it('call callback with the latest value if maxWait time exceed', () => { | ||
const callback = (value) => expect(value).toBe('Right value'); | ||
function Component({ text }) { | ||
const [debouncedCallback] = useDebouncedCallback(callback, 500, [], { maxWait: 600 }); | ||
debouncedCallback(text); | ||
return <span>{text}</span>; | ||
} | ||
const tree = Enzyme.mount(<Component text="Wrong Value" />); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
tree.setProps({ text: 'Right value' }); | ||
}); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
}); | ||
}); | ||
it('will call callback if maxWait time exceed', () => { | ||
const callback = jest.fn(); | ||
function Component({ text }) { | ||
const [debouncedCallback] = useDebouncedCallback(callback, 500, [], { maxWait: 600 }); | ||
debouncedCallback(); | ||
return <span>{text}</span>; | ||
} | ||
const tree = Enzyme.mount(<Component text="one" />); | ||
expect(callback.mock.calls.length).toBe(0); | ||
expect(tree.text()).toBe('one'); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
tree.setProps({ text: 'test' }); | ||
}); | ||
expect(callback.mock.calls.length).toBe(0); | ||
expect(tree.text()).toBe('test'); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
}); | ||
expect(callback.mock.calls.length).toBe(1); | ||
}); | ||
it('will cancel callback if maxWait time exceed and cancel method was invoked', () => { | ||
const callback = jest.fn(); | ||
function Component({ text }) { | ||
const [debouncedCallback, cancel] = useDebouncedCallback(callback, 500, [], { maxWait: 600 }); | ||
debouncedCallback(); | ||
if (text === 'test') { | ||
cancel(); | ||
} | ||
return <span>{text}</span>; | ||
} | ||
const tree = Enzyme.mount(<Component text="one" />); | ||
expect(callback.mock.calls.length).toBe(0); | ||
expect(tree.text()).toBe('one'); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
tree.setProps({ text: 'test' }); | ||
}); | ||
expect(callback.mock.calls.length).toBe(0); | ||
expect(tree.text()).toBe('test'); | ||
act(() => { | ||
jest.runTimersToTime(400); | ||
}); | ||
expect(callback.mock.calls.length).toBe(0); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
206563
360
16
120