Skip to content

Commit 2b87cd9

Browse files
ssi02014minsoo-webSangminnn
committed
feat(react): useInterval startInterval/stopInterval 함수 추가
Co-authored-by: Minsoo Kim <[email protected]> Co-authored-by: Sangminnn <[email protected]>
1 parent 1c389aa commit 2b87cd9

File tree

4 files changed

+80
-17
lines changed

4 files changed

+80
-17
lines changed

.changeset/strong-bulldogs-marry.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@modern-kit/react': patch
3+
---
4+
5+
feat(react): useInterval startInterval/stopInterval 함수 추가 - @ssi02014

docs/docs/react/hooks/useInterval.mdx

+24-12
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ type IntervalOptions =
3131
const useInterval: (
3232
callback: SetIntervalParameters[0],
3333
options: IntervalOptions
34-
) => void;
34+
) => {
35+
isActing: boolean;
36+
startInterval: () => void;
37+
stopInterval: () => void;
38+
};
3539
```
3640

3741
## Usage
@@ -40,16 +44,18 @@ import { useInterval } from '@modern-kit/react';
4044

4145
const Example = () => {
4246
const [number, setNumber] = useState(0);
43-
const [isToggle, setIsToggle] = useState(true);
4447

45-
useInterval(() => setNumber(number + 1), { delay: 1000, enabled: isToggle });
48+
const { isActing, stopInterval, startInterval } = useInterval(
49+
() => setNumber(number + 1),
50+
{ delay: 1000, enabled: false } // 자동 실행 X
51+
);
4652

4753
return (
4854
<div>
49-
<div>{number}</div>
50-
<button onClick={() => setIsToggle(!isToggle)}>
51-
{isToggle ? '멈추기' : '시작하기'}
52-
</button>
55+
<p>{number}</p>
56+
<p>{`isActing: ${isActing}`}</p>
57+
<button onClick={startInterval}>시작하기</button>
58+
<button onClick={stopInterval}>멈추기</button>
5359
</div>
5460
);
5561
};
@@ -58,13 +64,19 @@ const Example = () => {
5864

5965
export const Example = () => {
6066
const [number, setNumber] = useState(0);
61-
const [isToggle, setIsToggle] = useState(true);
62-
useInterval(() => setNumber(number + 1), { delay: 1000, enabled: isToggle });
67+
const { isActing, stopInterval, startInterval } = useInterval(
68+
() => setNumber(number + 1),
69+
{ delay: 1000, enabled: false }
70+
);
6371
return (
6472
<div>
65-
<div>{number}</div>
66-
<button onClick={() => setIsToggle(!isToggle)}>
67-
{isToggle ? '멈추기' : '시작하기'}
73+
<p>{number}</p>
74+
<p>{`isActing: ${isActing}`}</p>
75+
<button onClick={startInterval}>
76+
시작하기
77+
</button>
78+
<button onClick={stopInterval}>
79+
멈추기
6880
</button>
6981
</div>
7082
);

packages/react/src/hooks/useInterval/index.ts

+26-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import { useCallback, useEffect, useState } from 'react';
12
import { isNumber } from '@modern-kit/utils';
23
import { usePreservedCallback } from '../usePreservedCallback';
3-
import { useEffect } from 'react';
4+
import { usePreservedState } from '../usePreservedState';
45

56
type SetIntervalParameters = Parameters<typeof setInterval>;
67

@@ -12,11 +13,30 @@ export const useInterval = (
1213
callback: SetIntervalParameters[0],
1314
options: IntervalOptions
1415
) => {
15-
const delay = isNumber(options) ? options : options.delay;
16-
const enabled = isNumber(options) ? true : options?.enabled ?? true;
17-
16+
const preservedOptions = usePreservedState(options);
1817
const callbackAction = usePreservedCallback(callback);
1918

19+
const isNumberOptions = isNumber(preservedOptions);
20+
const delay = isNumberOptions ? preservedOptions : preservedOptions.delay;
21+
22+
const [enabled, setEnabled] = useState(() =>
23+
isNumberOptions ? true : preservedOptions?.enabled ?? true
24+
);
25+
26+
const startInterval = useCallback(() => {
27+
setEnabled(true);
28+
}, []);
29+
30+
const stopInterval = useCallback(() => {
31+
setEnabled(false);
32+
}, []);
33+
34+
useEffect(() => {
35+
if (!isNumberOptions) {
36+
setEnabled(preservedOptions?.enabled ?? true);
37+
}
38+
}, [isNumberOptions, preservedOptions]);
39+
2040
useEffect(() => {
2141
if (delay == null) return;
2242

@@ -30,4 +50,6 @@ export const useInterval = (
3050

3151
return () => clearInterval(intervalId);
3252
}, [callbackAction, enabled, delay]);
53+
54+
return { isActing: enabled, start: startInterval, stop: stopInterval };
3355
};

packages/react/src/hooks/useInterval/useInterval.spec.tsx

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { renderHook } from '@testing-library/react';
1+
import { act, renderHook } from '@testing-library/react';
22
import { useInterval } from '.';
33

44
const delayTime = 300;
@@ -60,4 +60,28 @@ describe('useInterval', () => {
6060
vi.advanceTimersByTime(delayTime);
6161
expect(mockFn).not.toBeCalled();
6262
});
63+
64+
it('should correctly start and stop the interval, calling the callback at the correct times', async () => {
65+
const { result } = renderHook(() =>
66+
useInterval(mockFn, { delay: delayTime, enabled: false })
67+
);
68+
69+
const startInterval = result.current.start;
70+
const stopInterval = result.current.stop;
71+
72+
vi.advanceTimersByTime(delayTime);
73+
expect(mockFn).not.toBeCalled();
74+
75+
act(() => startInterval());
76+
77+
vi.advanceTimersByTime(delayTime);
78+
expect(mockFn).toBeCalledTimes(1);
79+
expect(result.current.isActing).toBeTruthy();
80+
81+
act(() => stopInterval());
82+
83+
vi.advanceTimersByTime(delayTime);
84+
expect(mockFn).toBeCalledTimes(1);
85+
expect(result.current.isActing).toBeFalsy();
86+
});
6387
});

0 commit comments

Comments
 (0)