Skip to content

Commit

Permalink
Merge branch 'brickspert-feat/enhance_useDebounce'
Browse files Browse the repository at this point in the history
  • Loading branch information
xobotyi committed Nov 6, 2019
2 parents 1fdc907 + 53787c0 commit 27a5e5f
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 19 deletions.
24 changes: 18 additions & 6 deletions docs/useDebounce.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ The third argument is the array of values that the debounce depends on, in the s
## Usage

```jsx
import React, { useState } from 'react';
import { useDebounce } from 'react-use';

const Demo = () => {
const [state, setState] = React.useState('Typing stopped');
const [val, setVal] = React.useState('');
const [debouncedValue, setDebouncedValue] = React.useState('');

useDebounce(
const [, cancel] = useDebounce(
() => {
setState('Typing stopped');
setDebouncedValue(val);
Expand All @@ -36,7 +33,10 @@ const Demo = () => {
}}
/>
<div>{state}</div>
<div>Debounced value: {debouncedValue}</div>
<div>
Debounced value: {debouncedValue}
<button onClick={cancel}>Cancel debounce</button>
</div>
</div>
);
};
Expand All @@ -45,5 +45,17 @@ const Demo = () => {
## Reference

```ts
useDebounce(fn, ms: number, args: any[]);
const [
isReady: () => boolean | null,
cancel: () => void,
] = useDebounce(fn: Function, ms: number, deps: DependencyList = []);
```

- **`fn`**_`: Function`_ - function that will be called;
- **`ms`**_`: number`_ - delay in milliseconds;
- **`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
- `null` - cancelled
- **`cancel`**_`: ()=>void`_ - cancel the debounce
7 changes: 5 additions & 2 deletions src/__stories__/useDebounce.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const Demo = () => {
const [val, setVal] = React.useState('');
const [debouncedValue, setDebouncedValue] = React.useState('');

useDebounce(
const [, cancel] = useDebounce(
() => {
setState('Typing stopped');
setDebouncedValue(val);
Expand All @@ -29,7 +29,10 @@ const Demo = () => {
}}
/>
<div>{state}</div>
<div>Debounced value: {debouncedValue}</div>
<div>
Debounced value: {debouncedValue}
<button onClick={cancel}>Cancel debounce</button>
</div>
</div>
);
};
Expand Down
116 changes: 116 additions & 0 deletions src/__tests__/useDebounce.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
20 changes: 9 additions & 11 deletions src/useDebounce.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { DependencyList } from 'react';
import useUpdateEffect from './useUpdateEffect';
import { DependencyList, useEffect } from 'react';
import useTimeoutFn from './useTimeoutFn';

const useDebounce = (fn: () => any, ms: number = 0, deps: DependencyList = []) => {
useUpdateEffect(() => {
const timeout = setTimeout(fn, ms);
export type UseDebounceReturn = [() => boolean | null, () => void];

return () => {
clearTimeout(timeout);
};
}, deps);
};
export default function useDebounce(fn: Function, ms: number = 0, deps: DependencyList = []): UseDebounceReturn {
const [isReady, cancel, reset] = useTimeoutFn(fn, ms);

export default useDebounce;
useEffect(reset, deps);

return [isReady, cancel];
}

0 comments on commit 27a5e5f

Please sign in to comment.