1
- import { useState } from 'react' ;
2
- import { useInterval } from '../useInterval/useInterval' ;
1
+ import { useEffect , useRef , useState } from 'react' ;
3
2
export const getTimeFromSeconds = ( timestamp ) => {
4
3
const roundedTimestamp = Math . ceil ( timestamp ) ;
5
4
const days = Math . floor ( roundedTimestamp / ( 60 * 60 * 24 ) ) ;
@@ -23,48 +22,68 @@ export const getTimeFromSeconds = (timestamp) => {
23
22
* @param {() => void } callback The function to be executed once countdown timer is expired
24
23
*
25
24
* @example
26
- * const { days, hours, minutes, seconds, toggle, pause, start, restart, running } = useTimer(1000, () => console.log('ready'));
25
+ * const { days, hours, minutes, seconds, toggle, pause, start, restart, resume, active } = useTimer(1000, () => console.log('ready'));
27
26
*
28
27
* @overload
29
28
* @param {number } timestamp The timestamp value that define for how long the timer will be running
30
- * @param {boolean } options.autostart The flag to decide if timer should start automatically
29
+ * @param {boolean } options.immediately The flag to decide if timer should start automatically
31
30
* @param {() => void } options.onExpire The function to be executed when the timer is expired
32
31
* @param {(timestamp: number) => void } options.onTick The function to be executed on each tick of the timer
33
32
*
34
33
* @example
35
- * const { days, hours, minutes, seconds, toggle, pause, start, restart, running } = useTimer(1000);
34
+ * const { days, hours, minutes, seconds, toggle, pause, start, restart, resume, active } = useTimer(1000);
36
35
*/
37
36
export const useTimer = ( ( ...params ) => {
38
37
const timestamp = params [ 0 ] ;
39
38
const options = ( typeof params [ 1 ] === 'object' ? params [ 1 ] : { onExpire : params [ 1 ] } ) ;
40
- const autostart = options ?. autostart ?? true ;
39
+ const immediately = options ?. immediately ?? true ;
40
+ const [ active , setActive ] = useState ( immediately ?? true ) ;
41
41
const [ seconds , setSeconds ] = useState ( Math . ceil ( timestamp / 1000 ) ) ;
42
- const [ running , setRunning ] = useState ( autostart ) ;
43
- const restart = ( timestamp , autostart = true ) => {
42
+ const intervalIdRef = useRef ( ) ;
43
+ const optionsRef = useRef ( ) ;
44
+ optionsRef . current = options ?? { } ;
45
+ useEffect ( ( ) => {
46
+ if ( ! active )
47
+ return ;
48
+ const onInterval = ( ) => {
49
+ optionsRef . current ?. onTick ?. ( seconds ) ;
50
+ setSeconds ( ( prevSeconds ) => {
51
+ const updatedSeconds = prevSeconds - 1 ;
52
+ if ( updatedSeconds === 0 ) {
53
+ setActive ( false ) ;
54
+ optionsRef . current ?. onExpire ?. ( ) ;
55
+ }
56
+ return updatedSeconds ;
57
+ } ) ;
58
+ } ;
59
+ intervalIdRef . current = setInterval ( onInterval , 1000 ) ;
60
+ return ( ) => {
61
+ clearInterval ( intervalIdRef . current ) ;
62
+ } ;
63
+ } , [ active ] ) ;
64
+ const pause = ( ) => setActive ( false ) ;
65
+ const resume = ( ) => {
66
+ if ( seconds <= 0 )
67
+ return ;
68
+ setActive ( true ) ;
69
+ } ;
70
+ const toggle = ( ) => setActive ( ! active ) ;
71
+ const restart = ( timestamp , immediately = true ) => {
44
72
setSeconds ( Math . ceil ( timestamp / 1000 ) ) ;
45
- setRunning ( autostart ) ;
73
+ if ( immediately )
74
+ setActive ( true ) ;
46
75
} ;
47
76
const start = ( ) => {
48
- setRunning ( true ) ;
77
+ setActive ( true ) ;
49
78
setSeconds ( Math . ceil ( timestamp / 1000 ) ) ;
50
79
} ;
51
- useInterval ( ( ) => {
52
- options ?. onTick ?. ( seconds ) ;
53
- setSeconds ( ( prevSeconds ) => {
54
- const updatedSeconds = prevSeconds - 1 ;
55
- if ( updatedSeconds === 0 ) {
56
- setRunning ( false ) ;
57
- options ?. onExpire ?. ( ) ;
58
- }
59
- return updatedSeconds ;
60
- } ) ;
61
- } , 1000 , { enabled : running } ) ;
62
80
return {
63
81
...getTimeFromSeconds ( seconds ) ,
64
- pause : ( ) => setRunning ( false ) ,
65
- toggle : ( ) => setRunning ( ! running ) ,
82
+ pause,
83
+ active,
84
+ resume,
85
+ toggle,
66
86
start,
67
- restart,
68
- running
87
+ restart
69
88
} ;
70
89
} ) ;
0 commit comments