Skip to content

Commit cf9003b

Browse files
committed
main 🧊 use timer rework
1 parent b41eab9 commit cf9003b

File tree

10 files changed

+204
-172
lines changed

10 files changed

+204
-172
lines changed

‎packages/core/src/bundle/hooks/useInterval/useInterval.js

+10-13
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ import { useEffect, useRef, useState } from 'react';
77
* @overload
88
* @param {() => void} callback Any callback function
99
* @param {number} [interval=1000] Time in milliseconds
10-
* @param {boolean} [options.enabled=true] Start the interval immediately
10+
* @param {boolean} [options.immediately=true] Start the interval immediately
1111
* @returns {UseIntervalReturn}
1212
*
1313
* @example
14-
* const { active, pause, resume } = useInterval(() => console.log('inside interval'), 2500);
14+
* const { active, pause, resume, toggle } = useInterval(() => console.log('inside interval'), 2500);
1515
*
1616
* @overload
1717
* @param {() => void} callback Any callback function
1818
* @param {number} [options.interval=1000] Time in milliseconds
19-
* @param {boolean} [options.enabled=true] Start the interval immediately
19+
* @param {boolean} [options.immediately=true] Start the interval immediately
2020
*
2121
* @example
22-
* const { active, pause, resume } = useInterval(() => console.log('inside interval'), { interval: 2500 });
22+
* const { active, pause, resume, toggle } = useInterval(() => console.log('inside interval'), { interval: 2500 });
2323
*/
2424
export const useInterval = ((...params) => {
2525
const callback = params[0];
@@ -29,8 +29,8 @@ export const useInterval = ((...params) => {
2929
const options = typeof params[1] === 'object'
3030
? params[1]
3131
: params[2];
32-
const enabled = options?.enabled ?? true;
33-
const [active, setActive] = useState(enabled ?? true);
32+
const immediately = options?.immediately ?? true;
33+
const [active, setActive] = useState(immediately ?? true);
3434
const intervalIdRef = useRef();
3535
const internalCallbackRef = useRef(callback);
3636
internalCallbackRef.current = callback;
@@ -42,20 +42,17 @@ export const useInterval = ((...params) => {
4242
clearInterval(intervalIdRef.current);
4343
};
4444
}, [active, interval]);
45-
useEffect(() => {
46-
setActive(enabled);
47-
}, [enabled]);
48-
const pause = () => {
49-
setActive(false);
50-
};
45+
const pause = () => setActive(false);
5146
const resume = () => {
5247
if (interval <= 0)
5348
return;
5449
setActive(true);
5550
};
51+
const toggle = () => setActive(!active);
5652
return {
5753
active,
5854
pause,
59-
resume
55+
resume,
56+
toggle
6057
};
6158
});

‎packages/core/src/bundle/hooks/useMemory/useMemory.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ export const useMemory = () => {
1919
totalJSHeapSize: 0,
2020
usedJSHeapSize: 0
2121
});
22-
useInterval(() => setValue(performance.memory), 1000, { enabled: supported });
22+
useInterval(() => setValue(performance.memory), 1000, { immediately: supported });
2323
return { supported, value };
2424
};
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { useState } from 'react';
2-
import { useInterval } from '../useInterval/useInterval';
1+
import { useEffect, useRef, useState } from 'react';
32
export const getTimeFromSeconds = (timestamp) => {
43
const roundedTimestamp = Math.ceil(timestamp);
54
const days = Math.floor(roundedTimestamp / (60 * 60 * 24));
@@ -23,48 +22,68 @@ export const getTimeFromSeconds = (timestamp) => {
2322
* @param {() => void} callback The function to be executed once countdown timer is expired
2423
*
2524
* @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'));
2726
*
2827
* @overload
2928
* @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
3130
* @param {() => void} options.onExpire The function to be executed when the timer is expired
3231
* @param {(timestamp: number) => void} options.onTick The function to be executed on each tick of the timer
3332
*
3433
* @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);
3635
*/
3736
export const useTimer = ((...params) => {
3837
const timestamp = params[0];
3938
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);
4141
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) => {
4472
setSeconds(Math.ceil(timestamp / 1000));
45-
setRunning(autostart);
73+
if (immediately)
74+
setActive(true);
4675
};
4776
const start = () => {
48-
setRunning(true);
77+
setActive(true);
4978
setSeconds(Math.ceil(timestamp / 1000));
5079
};
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 });
6280
return {
6381
...getTimeFromSeconds(seconds),
64-
pause: () => setRunning(false),
65-
toggle: () => setRunning(!running),
82+
pause,
83+
active,
84+
resume,
85+
toggle,
6686
start,
67-
restart,
68-
running
87+
restart
6988
};
7089
});

‎packages/core/src/hooks/useInterval/useInterval.test.ts

+6-15
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ beforeEach(() => {
66
vi.useFakeTimers();
77
});
88

9+
afterEach(() => {
10+
vi.clearAllTimers();
11+
});
12+
913
it('Should use interval', () => {
1014
const { result } = renderHook(() => useInterval(vi.fn, 1000));
1115
expect(result.current.active).toBeTruthy();
@@ -14,7 +18,7 @@ it('Should use interval', () => {
1418
});
1519

1620
it('Should pause and resume properly', () => {
17-
const { result } = renderHook(() => useInterval(() => {}, 1000));
21+
const { result } = renderHook(() => useInterval(vi.fn, 1000));
1822
const { pause, resume } = result.current;
1923

2024
expect(result.current.active).toBeTruthy();
@@ -25,22 +29,9 @@ it('Should pause and resume properly', () => {
2529
});
2630

2731
it('Should not be active when disabled', () => {
28-
const { result } = renderHook(() => useInterval(() => {}, 1000, { enabled: false }));
29-
30-
expect(result.current.active).toBeFalsy();
31-
});
32-
33-
it('Should update active state on rerender', () => {
34-
const { result, rerender } = renderHook(
35-
(enabled: boolean) => useInterval(() => {}, 1000, { enabled }),
36-
{ initialProps: false }
37-
);
32+
const { result } = renderHook(() => useInterval(vi.fn, 1000, { immediately: false }));
3833

3934
expect(result.current.active).toBeFalsy();
40-
41-
rerender(true);
42-
43-
expect(result.current.active).toBeTruthy();
4435
});
4536

4637
it('Should call callback on interval', () => {

‎packages/core/src/hooks/useInterval/useInterval.ts

+14-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react';
33
/** The use interval options */
44
export interface UseIntervalOptions {
55
/** Start the interval immediately */
6-
enabled?: boolean;
6+
immediately?: boolean;
77
}
88

99
/** The use interval return type */
@@ -14,6 +14,8 @@ export interface UseIntervalReturn {
1414
pause: () => void;
1515
/** Resume the interval */
1616
resume: () => void;
17+
/** Toggle the interval */
18+
toggle: () => void;
1719
}
1820

1921
interface UseInterval {
@@ -30,19 +32,19 @@ interface UseInterval {
3032
* @overload
3133
* @param {() => void} callback Any callback function
3234
* @param {number} [interval=1000] Time in milliseconds
33-
* @param {boolean} [options.enabled=true] Start the interval immediately
35+
* @param {boolean} [options.immediately=true] Start the interval immediately
3436
* @returns {UseIntervalReturn}
3537
*
3638
* @example
37-
* const { active, pause, resume } = useInterval(() => console.log('inside interval'), 2500);
39+
* const { active, pause, resume, toggle } = useInterval(() => console.log('inside interval'), 2500);
3840
*
3941
* @overload
4042
* @param {() => void} callback Any callback function
4143
* @param {number} [options.interval=1000] Time in milliseconds
42-
* @param {boolean} [options.enabled=true] Start the interval immediately
44+
* @param {boolean} [options.immediately=true] Start the interval immediately
4345
*
4446
* @example
45-
* const { active, pause, resume } = useInterval(() => console.log('inside interval'), { interval: 2500 });
47+
* const { active, pause, resume, toggle } = useInterval(() => console.log('inside interval'), { interval: 2500 });
4648
*/
4749
export const useInterval = ((...params: any[]): UseIntervalReturn => {
4850
const callback = params[0] as () => void;
@@ -54,9 +56,9 @@ export const useInterval = ((...params: any[]): UseIntervalReturn => {
5456
typeof params[1] === 'object'
5557
? (params[1] as (UseIntervalOptions & { interval?: number }) | undefined)
5658
: (params[2] as UseIntervalOptions | undefined);
57-
const enabled = options?.enabled ?? true;
59+
const immediately = options?.immediately ?? true;
5860

59-
const [active, setActive] = useState<boolean>(enabled ?? true);
61+
const [active, setActive] = useState<boolean>(immediately ?? true);
6062

6163
const intervalIdRef = useRef<ReturnType<typeof setInterval>>();
6264
const internalCallbackRef = useRef(callback);
@@ -71,22 +73,19 @@ export const useInterval = ((...params: any[]): UseIntervalReturn => {
7173
};
7274
}, [active, interval]);
7375

74-
useEffect(() => {
75-
setActive(enabled);
76-
}, [enabled]);
77-
78-
const pause = () => {
79-
setActive(false);
80-
};
76+
const pause = () => setActive(false);
8177

8278
const resume = () => {
8379
if (interval <= 0) return;
8480
setActive(true);
8581
};
8682

83+
const toggle = () => setActive(!active);
84+
8785
return {
8886
active,
8987
pause,
90-
resume
88+
resume,
89+
toggle
9190
};
9291
}) as UseInterval;

‎packages/core/src/hooks/useMemory/useMemory.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const useMemory = (): UseMemoryReturn => {
4040
usedJSHeapSize: 0
4141
});
4242

43-
useInterval(() => setValue(performance.memory), 1000, { enabled: supported });
43+
useInterval(() => setValue(performance.memory), 1000, { immediately: supported });
4444

4545
return { supported, value };
4646
};

‎packages/core/src/hooks/useRenderInfo/useRenderInfo.test.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@ it('Should use render info', () => {
88

99
expect(result.current.component).toBe('Component');
1010
expect(result.current.renders).toBe(1);
11-
expect(result.current.timestamp).toBeTypeOf('number');
11+
expect(result.current.timestamp).toBe(921196800000);
1212
expect(result.current.sinceLast).toBe(0);
1313
});
1414

15-
it('Should increment renders on subsequent calls', () => {
15+
it('Should increment renders on subsequent calls', async () => {
1616
const { result, rerender } = renderHook(() => useRenderInfo('Component'));
1717

1818
expect(result.current.renders).toBe(1);
19+
expect(result.current.timestamp).toBe(921196800000);
20+
expect(result.current.sinceLast).toBe(0);
1921

22+
await vi.advanceTimersByTimeAsync(1);
2023
rerender();
2124

2225
expect(result.current.renders).toBe(2);
26+
expect(result.current.timestamp).toBe(921196800001);
27+
expect(result.current.sinceLast).toBe(1);
2328
});
2429

2530
it('Should log render information when log is true', () => {

‎packages/core/src/hooks/useTimer/useTimer.demo.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const Demo = () => {
1313
</code>
1414
</p>
1515
<p>
16-
Timer running: <code>{String(timer.running)}</code>
16+
Timer running: <code>{String(timer.active)}</code>
1717
</p>
1818

1919
<button type='button' onClick={timer.start}>

0 commit comments

Comments
 (0)