Skip to content

Commit 445f9fe

Browse files
authored
feat: useDelay hook (#1808)
useDelay hook that returns true until a delay timeout has completed then returns false. resolves #1807
1 parent 36bc251 commit 445f9fe

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

packages/react-hooks/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export * from './useAsyncInterval';
44
export * from './useCallbackWithAction';
55
export { default as useContextOrThrow } from './useContextOrThrow';
66
export * from './useDebouncedCallback';
7+
export * from './useDelay';
78
export * from './useDependentState';
89
export * from './useEffectNTimesWhen';
910
export * from './useIsEqualMemo';
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { act, renderHook } from '@testing-library/react-hooks';
2+
import { useDelay } from './useDelay';
3+
4+
describe('useDelay', () => {
5+
const delay500 = 500;
6+
const delay1000 = 1000;
7+
const delay2000 = 2000;
8+
9+
beforeEach(() => {
10+
jest.useFakeTimers();
11+
});
12+
13+
afterEach(() => {
14+
jest.useRealTimers();
15+
});
16+
17+
it('should return true while delay is active and false when done', () => {
18+
const { result } = renderHook(() => useDelay(delay1000));
19+
expect(result.current).toBe(true);
20+
21+
act(() => jest.advanceTimersByTime(delay1000));
22+
23+
expect(result.current).toBe(false);
24+
});
25+
26+
it('should cancel setter when unmounted', () => {
27+
const { result, unmount } = renderHook(() => useDelay(delay1000));
28+
unmount();
29+
30+
act(() => jest.advanceTimersByTime(delay1000));
31+
32+
expect(result.current).toBe(true);
33+
});
34+
35+
it('should reset when delayMs is changed', () => {
36+
const { result, rerender } = renderHook(
37+
({ delayMs }) => useDelay(delayMs),
38+
{
39+
initialProps: { delayMs: delay1000 },
40+
}
41+
);
42+
43+
act(() => jest.advanceTimersByTime(delay500));
44+
45+
rerender({ delayMs: delay2000 });
46+
47+
act(() => jest.advanceTimersByTime(delay1000));
48+
49+
expect(result.current).toBe(true);
50+
51+
act(() => jest.advanceTimersByTime(delay1000));
52+
53+
expect(result.current).toBe(false);
54+
});
55+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useEffect, useState } from 'react';
2+
3+
/**
4+
* Sets a delay, and returns a boolean indicating whether the delay is still active.
5+
* @param delayMs The delay in milliseconds
6+
* @returns A boolean indicating whether the delay is still active
7+
*/
8+
export function useDelay(delayMs: number): boolean {
9+
const [isDelayed, setIsDelayed] = useState(true);
10+
11+
useEffect(() => {
12+
const timeout = setTimeout(() => {
13+
setIsDelayed(false);
14+
}, delayMs);
15+
16+
return () => {
17+
clearTimeout(timeout);
18+
};
19+
}, [delayMs]);
20+
21+
return isDelayed;
22+
}
23+
24+
export default useDelay;

0 commit comments

Comments
 (0)