Merge pull request #568 from streamich/imp-useTimeout

fix: make useTimeoutFn work properly:
This commit is contained in:
Anton Zinovyev 2019-08-27 01:07:39 +03:00 committed by GitHub
commit d5ca05e231
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 10 deletions

View File

@ -1,10 +1,13 @@
# `useTimeoutFn`
Calls given function after specified amount of milliseconds.
**Note:** this hook does not re-render component by itself.
Calls given function after specified amount of milliseconds.
Automatically cancels timeout on component unmount.
Automatically resets timeout on delay change.
Several thing about it's work:
- does not re-render component;
- automatically cancel timeout on cancel;
- automatically reset timeout on delay change;
- reset function call will cancel previous timeout;
- timeout will NOT be reset on function change. It will be called within the timeout, you have to reset it on your own when needed.
## Usage

View File

@ -28,9 +28,11 @@ describe('useTimeoutFn', () => {
expect(typeof hook.result.current[2]).toBe('function');
});
function getHook(ms: number = 5): [jest.Mock, RenderHookResult<{ delay: number }, UseTimeoutFnReturn>] {
const spy = jest.fn();
return [spy, renderHook(({ delay = 5 }) => useTimeoutFn(spy, delay), { initialProps: { delay: ms } })];
function getHook(
ms: number = 5,
fn: Function = jest.fn()
): [Function, RenderHookResult<{ delay: number; cb: Function }, UseTimeoutFnReturn>] {
return [fn, renderHook(({ delay = 5, cb }) => useTimeoutFn(cb, delay), { initialProps: { delay: ms, cb: fn } })];
}
it('should call passed function after given amount of time', () => {
@ -108,9 +110,23 @@ describe('useTimeoutFn', () => {
const [spy, hook] = getHook(50);
expect(spy).not.toHaveBeenCalled();
hook.rerender({ delay: 5 });
hook.rerender({ delay: 5, cb: spy });
jest.advanceTimersByTime(5);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should NOT reset timeout on function change', () => {
const [spy, hook] = getHook(50);
jest.advanceTimersByTime(25);
expect(spy).not.toHaveBeenCalled();
const spy2 = jest.fn();
hook.rerender({ delay: 50, cb: spy2 });
jest.advanceTimersByTime(25);
expect(spy).not.toHaveBeenCalled();
expect(spy2).toHaveBeenCalledTimes(1);
});
});

View File

@ -5,20 +5,31 @@ export type UseTimeoutFnReturn = [() => boolean | null, () => void, () => void];
export default function useTimeoutFn(fn: Function, ms: number = 0): UseTimeoutFnReturn {
const ready = useRef<boolean | null>(false);
const timeout = useRef<ReturnType<typeof setTimeout>>();
const callback = useRef(fn);
const isReady = useCallback(() => ready.current, []);
const set = useCallback(() => {
ready.current = false;
timeout.current && clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
ready.current = true;
fn();
callback.current();
}, ms);
}, [ms, fn]);
}, [ms]);
const clear = useCallback(() => {
ready.current = null;
timeout.current && clearTimeout(timeout.current);
}, []);
// update ref when function changes
useEffect(() => {
callback.current = fn;
}, [fn]);
// set on mount, clear on unmount
useEffect(() => {
set();