Skip to content

Commit 49197b4

Browse files
committed
[IMPL] useTimeout and useInterval hooks
1 parent 0bc2248 commit 49197b4

12 files changed

Lines changed: 294 additions & 8 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { useState } from "react"
2+
import { useInterval } from "../../../../../../packages/react-tools/src";
3+
4+
/**
5+
The component has:
6+
- A _count_ state initialized to _0_.
7+
- A _interval_ state initialized to _1000_.
8+
- A _useInterval_ that receives a _callback_ to update _count_ by 1 and _interval_ as time to interval execution.
9+
- Two p tag to diplay current _count_ and _interval_ variable.
10+
- A button to execute _apply_ function returned from _useInterval_.
11+
- A button to increment by 1000 _interval_ state.
12+
- A button to restore _interval_ state to 1000.
13+
- A button to execute _clear_ function returned from _useInterval_.
14+
- A button to execute _applyPromisy_ function returned from _useInterval_ tha after _count_ state update, restore _interval_ state to 1000.
15+
*/
16+
export const UseInterval = () => {
17+
const [count, setCount] = useState(0);
18+
const [interval, setInterval] = useState(1000);
19+
const [apply, clear, applyPromisy] = useInterval(
20+
() => setCount(c => c + 1),
21+
interval
22+
)
23+
24+
return (
25+
<div style={{ display: "grid", gridTemplateRows: "auto auto auto", justifyContent: "center", gap: 50 }}>
26+
<p>Count: {count}</p>
27+
<p>interval: {interval}</p>
28+
<div style={{ display: "grid", gridTemplateRows: "auto auto", gridTemplateColumns: "auto auto auto", justifyContent: "center", gap: 50 }}>
29+
<button onClick={apply}>Increment count</button>
30+
<button onClick={clear}>Clear Interval</button>
31+
<button onClick={async()=>applyPromisy().then(()=>setInterval(1000))}>Increment promisy</button>
32+
<button onClick={() => setInterval(d => d + 1000)}>Increment interval</button>
33+
<button onClick={() => setInterval(() => 1000)}>Clear interval</button>
34+
</div>
35+
</div>
36+
);
37+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { useState } from "react"
2+
import { useTimeout } from "../../../../../../packages/react-tools/src";
3+
4+
/**
5+
The component has:
6+
- A _count_ state initialized to _0_.
7+
- A _delay_ state initialized to _1000_.
8+
- A _useTimeout_ that receives a _callback_ to update _count_ by 1 and _delay_ as time to delay execution.
9+
- Two p tag to diplay current _count_ and _delay_ variable.
10+
- A button to execute _apply_ function returned from _useTimeout_.
11+
- A button to increment by 1000 _delay_ state.
12+
- A button to restore _delay_ state to 1000.
13+
- A button to execute _clear_ function returned from _useTimeout_.
14+
- A button to execute _applyPromisy_ function returned from _useTimeout_ tha after _count_ state update, restore _delay_ state to 1000.
15+
*/
16+
export const UseTimeout = () => {
17+
const [count, setCount] = useState(0);
18+
const [delay, setDelay] = useState(1000);
19+
const [apply, clear, applyPromisy] = useTimeout(
20+
() => setCount(c => c + 1),
21+
delay
22+
)
23+
24+
return (
25+
<div style={{ display: "grid", gridTemplateRows: "auto auto auto", justifyContent: "center", gap: 50 }}>
26+
<p>Count: {count}</p>
27+
<p>Delay: {delay}</p>
28+
<div style={{ display: "grid", gridTemplateRows: "auto auto", gridTemplateColumns: "auto auto auto", justifyContent: "center", gap: 50 }}>
29+
<button onClick={apply}>Increment count</button>
30+
<button onClick={clear}>Clear Timeout</button>
31+
<button onClick={async()=>applyPromisy().then(()=>setDelay(1000))}>Increment promisy</button>
32+
<button onClick={() => setDelay(d => d + 1000)}>Increment delay</button>
33+
<button onClick={() => setDelay(() => 1000)}>Clear delay</button>
34+
</div>
35+
</div>
36+
);
37+
}

apps/react-tools-demo/src/constants/components.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ export const COMPONENTS = [
5151
"useScript",
5252
"useDebounce",
5353
"useThrottle",
54-
"useActiveElement"
54+
"useActiveElement",
55+
"useTimeout",
56+
"useInterval"
5557
]
5658
],
5759
//UTILS

apps/react-tools-demo/src/markdown/useActiveElement.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const UseActiveElement = () => {
1818
}
1919
```
2020

21-
> The component has 3 HTML elements: a _span_ element with __tabindex__ attribute, a _p_ element with __tabindex__ attribute and a _button_ element. It renders also the current active element. If element focused changes, active element displayed will change.
21+
> The component has 3 HTML elements: a _span_ element with __tabindex__ attribute, a _p_ element with __tabindex__ attribute and a _button_ element. It renders also the current _active element_. If element focused changes, _active element_ displayed will change.
2222
2323

2424
## API
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# useInterval
2+
Hook to handle setInterval timer function with the possibility to clear and promisify execution.
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseInterval = () => {
8+
const [count, setCount] = useState(0);
9+
const [interval, setInterval] = useState(1000);
10+
const [apply, clear, applyPromisy] = useInterval(
11+
() => setCount(c => c + 1),
12+
interval
13+
)
14+
15+
return (
16+
<div style={{ display: "grid", gridTemplateRows: "auto auto auto", justifyContent: "center", gap: 50 }}>
17+
<p>Count: {count}</p>
18+
<p>interval: {interval}</p>
19+
<div style={{ display: "grid", gridTemplateRows: "auto auto", gridTemplateColumns: "auto auto auto", justifyContent: "center", gap: 50 }}>
20+
<button onClick={apply}>Increment count</button>
21+
<button onClick={clear}>Clear Interval</button>
22+
<button onClick={async()=>applyPromisy().then(()=>setInterval(1000))}>Increment promisy</button>
23+
<button onClick={() => setInterval(d => d + 1000)}>Increment interval</button>
24+
<button onClick={() => setInterval(() => 1000)}>Clear interval</button>
25+
</div>
26+
</div>
27+
);
28+
}
29+
```
30+
31+
> The component has:
32+
> - A _count_ state initialized to _0_.
33+
> - A _interval_ state initialized to _1000_.
34+
> - A _useInterval_ that receives a _callback_ to update _count_ by 1 and _interval_ as time to interval execution.
35+
> - Two p tag to diplay current _count_ and _interval_ variable.
36+
> - A button to execute _apply_ function returned from _useInterval_.
37+
> - A button to increment by 1000 _interval_ state.
38+
> - A button to restore _interval_ state to 1000.
39+
> - A button to execute _clear_ function returned from _useInterval_.
40+
> - A button to execute _applyPromisy_ function returned from _useInterval_ tha after _count_ state update, restore _interval_ state to 1000.
41+
42+
43+
## API
44+
45+
```tsx
46+
useInterval <TArgs extends unknown[]>(callback: (...args: TArgs) => void, delay: number): [(...args: TArgs) => void, () => void, (...args: TArgs) => Promise<void>]
47+
```
48+
49+
> ### Params
50+
>
51+
> - __callback__: _(...args: unknown[])=>void_
52+
Function to call when the timer elapses.
53+
> - __delay__: _number_
54+
The number of milliseconds to wait before calling the `callback`.
55+
>
56+
57+
> ### Returns
58+
>
59+
> __- array: first element is the function to call setInterval; second element is the function to clearInterval; thrid element promisify setInterval.__
60+
> - __Array__:
61+
> - _(...args: TArgs) => void, () => void, (...args: TArgs) => Promise<void>_
62+
>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# useTimeout
2+
Hook to handle setTimeout timer function with the possibility to clear and promisify execution.
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseTimeout = () => {
8+
const [count, setCount] = useState(0);
9+
const [delay, setDelay] = useState(1000);
10+
const [apply, clear, applyPromisy] = useTimeout(
11+
() => setCount(c => c + 1),
12+
delay
13+
)
14+
15+
return (
16+
<div style={{ display: "grid", gridTemplateRows: "auto auto auto", justifyContent: "center", gap: 50 }}>
17+
<p>Count: {count}</p>
18+
<p>Delay: {delay}</p>
19+
<div style={{ display: "grid", gridTemplateRows: "auto auto", gridTemplateColumns: "auto auto auto", justifyContent: "center", gap: 50 }}>
20+
<button onClick={apply}>Increment count</button>
21+
<button onClick={clear}>Clear Timeout</button>
22+
<button onClick={async()=>applyPromisy().then(()=>setDelay(1000))}>Increment promisy</button>
23+
<button onClick={() => setDelay(d => d + 1000)}>Increment delay</button>
24+
<button onClick={() => setDelay(() => 1000)}>Clear delay</button>
25+
</div>
26+
</div>
27+
);
28+
}
29+
```
30+
31+
> The component has:
32+
> - A _count_ state initialized to _0_.
33+
> - A _delay_ state initialized to _1000_.
34+
> - A _useTimeout_ that receives a _callback_ to update _count_ by 1 and _delay_ as time to delay execution.
35+
> - Two p tag to diplay current _count_ and _delay_ variable.
36+
> - A button to execute _apply_ function returned from _useTimeout_.
37+
> - A button to increment by 1000 _delay_ state.
38+
> - A button to restore _delay_ state to 1000.
39+
> - A button to execute _clear_ function returned from _useTimeout_.
40+
> - A button to execute _applyPromisy_ function returned from _useTimeout_ tha after _count_ state update, restore _delay_ state to 1000.
41+
42+
43+
## API
44+
45+
```tsx
46+
useTimeout <TArgs extends unknown[]>(callback: (...args: TArgs) => void, delay: number): [(...args: TArgs) => void, () => void, (...args: TArgs) => Promise<void>]
47+
```
48+
49+
> ### Params
50+
>
51+
> - __callback__: _(...args: unknown[])=>void_
52+
Function to call when the timer elapses.
53+
> - __delay__: _number_
54+
The number of milliseconds to wait before calling the `callback`.
55+
>
56+
57+
> ### Returns
58+
>
59+
> __- array: first element is the function to call setTimeout; second element is the function to clearTimeout; thrid element promisify setTimeout.__
60+
> - __Array__:
61+
> - _(...args: TArgs) => void, () => void, (...args: TArgs) => Promise<void>_
62+
>

packages/react-tools/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- [x] useLayoutEffectOnce
2727
- [x] useUpdate
2828
- [x] useIsMounted
29+
- [ ] useDeferredValue (polyfill - an idea can be use setTimeout inside useEffect)
2930

3031
- __PERFORMANCE__
3132
- [x] useCallbackCompare
@@ -53,16 +54,16 @@
5354
- [ ] useDragAndDrop (check for mobile usage)
5455
- [ ] useInViewport (with area ratio of element: check if is equal to useIntersectionObserver)
5556
- [ ] useLongPress
56-
- [ ] useDeferredValue (polyfill - an idea can be use setTimeout inside useEffect)
57+
5758

5859
- __API DOM__
5960
- [x] usePublishSubscribe
6061
- [x] useScript
6162
- [x] useDebounce
6263
- [x] useThrottle
6364
- [x] useActiveElement
64-
- [ ] useTimeout
65-
- [ ] useInterval
65+
- [x] useTimeout
66+
- [x] useInterval
6667
- [ ] useTextSelection
6768
- [ ] useDocumentVisibility
6869
- [ ] useCoptyToClipboard

packages/react-tools/src/hooks/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ export { useSyncExternalStore } from './useSyncExternalStore';
3232
export { useIsMounted } from './useIsMounted';
3333
export { useDebounce } from './useDebounce';
3434
export { useThrottle } from './useThrottle';
35-
export { useActiveElement } from './useActiveElement';
35+
export { useActiveElement } from './useActiveElement';
36+
export { useTimeout } from './useTimeout';
37+
export { useInterval } from './useInterval';

packages/react-tools/src/hooks/useActiveElement.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const useActiveElement = ():Element|null => {
1111
return useSyncExternalStore(
1212
useCallback(notif => {
1313
previousElem.current = document.activeElement;
14-
const idTimeout:{id: number | NodeJS.Timeout} = { id: 0 };
14+
const idTimeout: { id: number | NodeJS.Timeout } = { id: 0 };
1515
const listener = (e: Event) => {
1616
/**
1717
* INFO
@@ -33,6 +33,9 @@ export const useActiveElement = ():Element|null => {
3333
notif();
3434
}
3535
}
36+
if (typeof addEventListener === "undefined") {
37+
throw Error("useActiveElement: hook works only in browser context.");
38+
}
3639
addEventListener("focusin", listener, true);
3740
addEventListener("focusout", listener, true);
3841

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useCallback, useEffect, useRef } from "react"
2+
3+
/**
4+
* **`useInterval`**: Hook to handle setInterval timer function with the possibility to clear and promisify execution.
5+
* @param {(...args: unknown[])=>void} callback - Function to call when the timer elapses.
6+
* @param {number} delay - The number of milliseconds to wait before calling the `callback`.
7+
* @returns {[(...args: TArgs) => void, () => void, (...args: TArgs) => Promise<void>]} - array: first element is the function to call setInterval; second element is the function to clearInterval; thrid element promisify setInterval.
8+
*/
9+
export const useInterval = <TArgs extends unknown[]>(callback: (...args: TArgs) => void, delay: number): [(...args: TArgs) => void, () => void, (...args: TArgs) => Promise<void>] => {
10+
const idInterval = useRef<number | NodeJS.Timeout>();
11+
12+
const apply = useCallback((...args: TArgs): void => {
13+
clearInterval(idInterval.current);
14+
idInterval.current = setInterval(() => callback(...args), delay);
15+
}, [callback, delay]);
16+
17+
const applyPromisify = useCallback((...args: TArgs): Promise<void> => {
18+
clearInterval(idInterval.current);
19+
return new Promise<void>(res => {
20+
idInterval.current = setInterval(() => {
21+
res(callback(...args));
22+
}, delay);
23+
})
24+
}, [callback, delay]);
25+
26+
const clear = useCallback(() => {
27+
clearInterval(idInterval.current);
28+
}, []);
29+
30+
useEffect(() => {
31+
clearInterval(idInterval.current);
32+
}, []);
33+
34+
return [
35+
apply,
36+
clear,
37+
applyPromisify
38+
];
39+
}

0 commit comments

Comments
 (0)