useDebounce:

- tests;
- fix function change debounce clear and reset;
- declare return type;
This commit is contained in:
xobotyi 2019-08-26 09:39:22 +03:00
parent 00e0c2691b
commit 42fdec140b
3 changed files with 129 additions and 6 deletions

View File

@ -48,12 +48,12 @@ const Demo = () => {
const [
isReady: () => boolean | null,
cancel: () => void,
] = useDebounce(fn: Function, ms: number, args: DependencyList = []);
] = useDebounce(fn: Function, ms: number, deps: DependencyList = []);
```
- **`fn`**_`: Function`_ - function that will be called;
- **`ms`**_`: number`_ - delay in milliseconds;
- **`args`**_`: DependencyList`_ - array of values that the debounce depends on, in the same manner as useEffect;
- **`deps`**_`: DependencyList`_ - array of values that the debounce depends on, in the same manner as useEffect;
- **`isReady`**_`: ()=>boolean|null`_ - function returning current debounce state:
- `false` - pending
- `true` - called

View File

@ -0,0 +1,116 @@
import { act, renderHook, RenderHookResult } from '@testing-library/react-hooks';
import { DependencyList } from 'react';
import { useDebounce } from '../index';
import { UseDebounceReturn } from '../useDebounce';
describe('useDebounce', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it('should be defined', () => {
expect(useDebounce).toBeDefined();
});
it('should return three functions', () => {
const hook = renderHook(() => useDebounce(() => {}, 5));
expect(hook.result.current.length).toBe(2);
expect(typeof hook.result.current[0]).toBe('function');
expect(typeof hook.result.current[1]).toBe('function');
});
function getHook(
ms: number = 5,
dep: DependencyList = []
): [jest.Mock, RenderHookResult<{ delay: number; deps: DependencyList }, UseDebounceReturn>] {
const spy = jest.fn();
return [
spy,
renderHook(({ delay = 5, deps = [] }) => useDebounce(spy, delay, deps), {
initialProps: {
delay: ms,
deps: dep,
},
}),
];
}
it('should call passed function after given amount of time', () => {
const [spy] = getHook();
expect(spy).not.toHaveBeenCalled();
jest.advanceTimersByTime(5);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should cancel function call on unmount', () => {
const [spy, hook] = getHook();
expect(spy).not.toHaveBeenCalled();
hook.unmount();
jest.advanceTimersByTime(5);
expect(spy).not.toHaveBeenCalled();
});
it('first function should return actual state of debounce', () => {
let [, hook] = getHook();
let [isReady] = hook.result.current;
expect(isReady()).toBe(false);
hook.unmount();
expect(isReady()).toBe(null);
[, hook] = getHook();
[isReady] = hook.result.current;
jest.advanceTimersByTime(5);
expect(isReady()).toBe(true);
});
it('second function should cancel debounce', () => {
const [spy, hook] = getHook();
const [isReady, cancel] = hook.result.current;
expect(spy).not.toHaveBeenCalled();
expect(isReady()).toBe(false);
act(() => {
cancel();
});
jest.advanceTimersByTime(5);
expect(spy).not.toHaveBeenCalled();
expect(isReady()).toBe(null);
});
it('should reset timeout on delay change', () => {
const [spy, hook] = getHook(50);
expect(spy).not.toHaveBeenCalled();
hook.rerender({ delay: 5, deps: [] });
jest.advanceTimersByTime(5);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should reset timeout on deps change', () => {
const [spy, hook] = getHook(50, [5, 6]);
jest.advanceTimersByTime(45);
expect(spy).not.toHaveBeenCalled();
hook.rerender({ delay: 50, deps: [6, 6] });
jest.advanceTimersByTime(45);
expect(spy).not.toHaveBeenCalled();
jest.advanceTimersByTime(5);
expect(spy).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,9 +1,16 @@
import { DependencyList, useCallback } from 'react';
import { DependencyList, useEffect } from 'react';
import useTimeoutFn from './useTimeoutFn';
export default function useDebounce(fn: (...args: any[]) => any, ms: number = 0, args: DependencyList = []) {
const cb = useCallback(fn, args);
const [isReady, cancel] = useTimeoutFn(cb, ms);
export type UseDebounceReturn = [() => boolean | null, () => void];
export default function useDebounce(
fn: (...args: any[]) => any,
ms: number = 0,
deps: DependencyList = []
): UseDebounceReturn {
const [isReady, cancel, reset] = useTimeoutFn(fn, ms);
useEffect(reset, deps);
return [isReady, cancel];
}