Skip to content

Commit 983b8b2

Browse files
authored
Merge pull request #92 from frontend-opensource-project/URH-105/use-async-tasks
[URH-105] useAsyncTasks 문서 작성 & 버그 픽스
2 parents 9a4c52d + df7c7d5 commit 983b8b2

File tree

5 files changed

+100
-13
lines changed

5 files changed

+100
-13
lines changed

docs/pages/hooks/_meta.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"useAsyncTasks": "useAsyncTasks",
23
"useClipboard": "useClipboard",
34
"useConfirm": "useConfirm",
45
"useDebounce": "useDebounce",
@@ -7,12 +8,16 @@
78
"useDetectDevice": "useDetectDevice",
89
"useGeolocation": "useGeolocation",
910
"useHover": "useHover",
11+
"useIntersectionObserver": "useIntersectionObserver",
1012
"useInterval": "useInterval",
1113
"useKeyCombination": "useKeyCombination",
14+
"useKeyDown": "useKeyDown",
15+
"useLocalStorage": "useLocalStorage",
1216
"useLongPress": "useLongPress",
17+
"useMockData": "useMockData",
1318
"useMousePosition": "useMousePosition",
14-
"useIntersectionObserver": "useIntersectionObserver",
1519
"useOnlineStatus": "useOnlineStatus",
20+
"useOutsideClick": "useOutsideClick",
1621
"usePrefersColorScheme": "usePrefersColorScheme",
1722
"usePreventCopy": "usePreventCopy",
1823
"useScrollLock": "useScrollLock",
@@ -22,11 +27,7 @@
2227
"useToggle": "useToggle",
2328
"useTranslation": "useTranslation",
2429
"useUnmountEffect": "useUnmountEffect",
25-
"useOutsideClick": "useOutsideClick",
26-
"useKeyDown": "useKeyDown",
27-
"useMockData": "useMockData",
2830
"useWorker": "useWorker",
29-
"useLocalStorage": "useLocalStorage",
3031
"_template": {
3132
"display": "hidden"
3233
}

docs/pages/hooks/useAsyncTasks.mdx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# useAsyncTasks
2+
3+
## Introduce
4+
5+
비동기 작업 리스트와 옵션을 받아 로딩 상태, 데이터, 오류 등을 관리하며, 컴포넌트 언마운트 시 상태 업데이트를 방지합니다.
6+
7+
```tsx
8+
interface UseAsyncTasksProps<R> {
9+
tasks: Task<R>[];
10+
options?: Options<R>;
11+
}
12+
13+
interface UseAsyncTasksReturns<R> {
14+
isLoading: boolean;
15+
data: R | null;
16+
error: Error | null;
17+
isError: boolean;
18+
reset: () => void;
19+
}
20+
21+
const useAsyncTasks = <R>(tasks: Task<R>[], options: Options<R>): UseAsyncTasksReturns<R> => { ... }
22+
```
23+
24+
### Props
25+
26+
- `tasks` : 실행할 비동기 작업들의 배열입니다. 비동기 함수 외에도 일반 함수, 원시 데이터 등을 전달할 수 있습니다.
27+
28+
### Returns
29+
30+
- `isLoading` : 현재 비동기 작업이 진행 중인지 여부를 나타내는 불리언 값입니다.
31+
- `data` : 성공적으로 완료된 작업의 결과를 담고 있는 데이터입니다.
32+
- `error` : 작업 중 발생한 오류 정보를 포함합니다.
33+
- `isError` : 오류가 발생했는지 여부를 나타내는 불리언 값입니다.
34+
- `reset` : 이전 상태를 초기화하여 초기 상태로 되돌리는 함수입니다.
35+
36+
## Examples
37+
38+
```tsx copy filename="TestComponent.tsx"
39+
const fetchUsers = async () => {
40+
const response = await fetch(`https://jsonplaceholder.typicode.com/users`);
41+
if (!response.ok) throw new Error('사용자 목록을 가져오지 못했습니다.');
42+
return response.json();
43+
};
44+
45+
const TestComponent = () => {
46+
const { isLoading, data, error, isError, reset } = useAsyncTasks<
47+
{ id: string; name: string }[]
48+
>([fetchUsers], {
49+
onSuccess: () => console.log('사용자 목록을 성공적으로 가져왔습니다!'),
50+
onError: (err) => console.error('오류:', err),
51+
});
52+
53+
return (
54+
<div>
55+
<button onClick={reset}>재로드</button>
56+
{isLoading && <p>로딩 중...</p>}
57+
{isError && <p>오류: {error?.message}</p>}
58+
{data && (
59+
<ul>
60+
{data.map((user) => (
61+
<li key={user.id}>{user.name}</li>
62+
))}
63+
</ul>
64+
)}
65+
</div>
66+
);
67+
};
68+
```

src/hooks/useAsyncTasks/reducer.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,20 @@ export function reducer<R>(
99
return { ...state, isLoading: true, error: null };
1010
case ACTION_TYPES.SUCCESS:
1111
return {
12+
...state,
1213
isLoading: false,
1314
data: action.payload,
1415
error: null,
1516
};
1617
case ACTION_TYPES.ERROR:
1718
return { ...state, isLoading: false, error: action.payload };
1819
case ACTION_TYPES.RESET:
19-
return { isLoading: false, data: null, error: null };
20+
return {
21+
isLoading: false,
22+
data: null,
23+
error: null,
24+
resetTrigger: state.resetTrigger + 1,
25+
};
2026
default:
2127
throw new Error('Unhandled task action type');
2228
}

src/hooks/useAsyncTasks/type.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface TaskState<R> {
3636
isLoading: boolean;
3737
data: R | null;
3838
error: PromiseCircularityError | null;
39+
resetTrigger: number;
3940
}
4041

4142
export type TaskAction<R> =

src/hooks/useAsyncTasks/useAsyncTasks.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,38 @@ import {
66
PromiseCircularityError,
77
} from 'async-wave';
88
import { delayExecution } from '@/utils';
9+
910
import { Task, Options, StateInfo, TaskAction, ACTION_TYPES } from './type';
1011
import { reducer } from './reducer';
1112

1213
/**
13-
* useAsyncTasks
14+
* useAsyncTasks: 비동기 작업 리스트와 옵션을 받아 상태 정보를 관리하는 훅
15+
*
1416
* @param {Task<R>[]} tasks - 비동기 작업 리스트
15-
* @param {Options<R>} options - 비동기 작업 옵션
17+
* - 실행할 비동기 작업들을 담은 배열입니다.
18+
* @param {Options<R>} [options={}] - 비동기 작업 옵션
19+
* - 비동기 작업 실행 중 설정할 옵션. 선택적이며 기본값은 빈 객체입니다.
20+
*
1621
* @returns {StateInfo<R>} 비동기 작업 상태 정보를 반환
22+
* - 작업의 로딩 상태, 데이터, 오류 정보를 포함합니다.
23+
* - 추가적으로 `isError` 속성을 통해 오류 여부를 확인할 수 있고, `reset` 메서드를 통해 상태를 초기화할 수 있습니다.
24+
*
25+
* @description
26+
* - 비동기 작업을 관리하며, 작업의 상태 정보(로딩, 데이터, 오류)를 제공합니다.
27+
* - 컴포넌트 언마운트 시, 비동기 작업이 완료된 후 상태 업데이트를 방지합니다.
28+
* - `reset` 메서드를 호출하여 상태를 초기화할 수 있습니다.
1729
*/
1830
const useAsyncTasks = <R>(tasks: Task<R>[], options: Options<R>) => {
1931
const isMountedRef = useRef(false); // 컴포넌트가 언마운트된 후 비동기 작업이 완료될 때 상태 업데이트를 방지
2032
const [state, dispatch] = useReducer(reducer<R>, {
2133
isLoading: false,
2234
data: null,
2335
error: null,
36+
resetTrigger: 0,
2437
});
2538

2639
useEffect(() => {
2740
isMountedRef.current = true;
28-
2941
(async () => {
3042
await asyncWave<R>(
3143
[...tasks],
@@ -37,16 +49,15 @@ const useAsyncTasks = <R>(tasks: Task<R>[], options: Options<R>) => {
3749
isMountedRef.current = false;
3850
};
3951
// eslint-disable-next-line react-hooks/exhaustive-deps
40-
}, []);
52+
}, [state.resetTrigger]);
4153

4254
const stateInfo: StateInfo<R> = useMemo(
4355
() => ({
4456
...state,
4557
isError: Boolean(state.error),
4658
reset() {
47-
dispatch({
48-
type: ACTION_TYPES.RESET,
49-
});
59+
dispatch({ type: ACTION_TYPES.RESET });
60+
isMountedRef.current = false;
5061
},
5162
}),
5263
[state]

0 commit comments

Comments
 (0)