![[React] getFetch 커스텀 훅 hook 만들기](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7C1i9%2FbtsHLW3sKm1%2FRu3W786evAhOu5ZrJuxrj1%2Fimg.png)
시작하기 앞서...
React 에서 get 요청을 할 때, 로딩여부인 isLoading, 에러 객체 정보가 저장된 loadError, 데이터를 로드하는 함수인 handleLoad 를 반환하는 useFetch 라는 커스텀 훅을 만들어 사용하였다.
import { useState } from 'react';
const useFetch = (getData) => {
const [isLoading, setIsLoading] = useState(false);
const [loadError, setLoadError] = useState(null);
const handleLoad = async (...arg) => {
try {
setLoadError(null);
setIsLoading(true);
let result = await getData(...arg);
return result;
} catch (error) {
setLoadError(error);
return null;
} finally {
setIsLoading(false);
}
};
return { isLoading, loadError, handleLoad };
};
export default useFetch;
위에서 만든 커스텀 hook 단점
그런데 이 커스텀 훅을 사용하는 곳에서도 또 훅(useState, useEffect)을 사용해서 데이터를 요청하게 되어 커스텀 훅은 만들었지만 효과적이지 않다는 단점이 있다.
const [isLoading, loadingError, handleLoad] = useLoad(getDonations);
const [donationList, setDonationList] = useState(null);
const handleDonationLoad = async () => {
const donations = await handleLoad();
if (donations) {
setDonationList(donations.list);
}
};
useEffect(() => {
handleDonationLoad();
}, []);
해당 커스텀 훅은 API 호출의 결과를 관리하지 않았기 때문에, 데이터를 로드하는 시점 컴포넌트에서 직접 제어해야 되어 비효율적이게 된다. 그래서 handleLoad 대신 결과인 data 를 반환하는 getFetch 커스텀 훅을 새롭게 만들게 되었다.
새로운 useFetch 커스텀 hook 만들기
새로운 커스텀 훅에서는 API 호출 결과인 data 도 같이 반환한다.
그러면 컴포넌트가 렌더링될 때 훅에서 자동으로 data 를 로드하고 data 의 상태관리도 할 수 있게 된다는 장점이 있다.
(만약 API GET 요청에 params 가 필요하지 않으면 args 만들 필요 없음.)
import { useEffect, useState } from 'react';
interface UseFetchReturn<T> {
data: T | null;
isLoading: boolean;
loadError: Error | null;
}
export const useFetch = <T>(
fetchFunction: (args: any) => Promise<T>,
args: unknown
): UseFetchReturn<T> => {
const [data, setData] = useState<T | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [loadError, setLoadError] = useState<Error | null>(null);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
setLoadError(null);
try {
const result = await fetchFunction(args);
setData(result);
} catch (error) {
setLoadError(error as Error);
} finally {
setIsLoading(false);
}
};
fetchData();
}, [args, fetchFunction]);
return { data, isLoading, loadError };
};
해당 훅을 사용하는 컴포넌트에서는 아래 코드처럼 사용하면 된다. 마치 React Query 의 data, isPending, error 와 비슷하다고 생각할 수 있다.
const params = useMemo(
() => ({
orderBy,
pageSize: 10,
keyword,
}),
[orderBy, keyword]
);
const { data, isLoading, loadError } = useFetch<{ list: Post[] }>(
getArticleList,
params
);
실제로 orderBy 와 keyword 의 값이 바뀔 때마다 API 호출이 제대로 된다.
새로운 커스텀 hook 단점
그러나 새로 만든 커스텀 hook 을 사용할 때 args 값을 넘겨줘야 하며, 해당 훅을 사용하는 컴포넌트에서는 params 에 useMemo 를 사용해서 넘겨줘야 하는 단점이 있다. useMemo 를 사용하지 않으면 params 가 매번 새로운 참조를 가지게 되어 useEffect 가 무한호출이 되어 API 도 무한호출이 된다. 😱
그래서 useMemo 를 사용하여 params 객체가 orderBy 나 keyword 가 변경될 때만 새롭게 생성되도록 한다. 이를 통해 params 객체의 참조가 불필요하게 변경되지 않아 useEffect 도 불필요하게 재실행되지 않아 API 무한호출이 되지 않게 된다. 만약 params(args)가 필요하지 않다면 빈객체를 {} useMemo 로 감싸서 넘겨줘야 한다.
마치며...
사실 useMemo 사용하는 게 번거로워서 계속 수정해보려고 했다. 그런데 실제 React Query 에서는 key 를 만들어서 변경을 체크하는데 내가 이를 직접 구현하기엔 아직 까다로워 보이고 나중에 React Query 를 사용하게 되니깐 일단 간단하게 여기까지 직접 만들어 보았다.
그래서 커스텀 훅으로 API 요청을 간단하게 할 수 있고 다른 파일에서도 요청을 할 수 있도록 하였다. 그 밖에도 useIsMobile, useBoolean 등 다양한 커스텀 훅들을 만들어 사용하고 있는데 확실히 코드가 짧아지고, 여러 곳에서 재사용할 수 있어 유용하다!

'💜 프로젝트 구현' 카테고리의 다른 글
프로젝트 초기 세팅 (with 템플릿, eslint, prettier, commitLint, tailwind) (1) | 2024.07.10 |
---|---|
메뉴바(드롭다운) 외부 바깥 영역 클릭 시 닫히기 (feat. useRef) (1) | 2024.07.08 |
[Github Action] Organization 레포지토리 vercel 미리보기 preview 배포 (0) | 2024.05.09 |
[Github Action] 깃허브 Organization 레포지토리 vercel 자동 배포 (0) | 2024.05.07 |
Zod enum 타입 error message 변경 방법 (0) | 2024.03.24 |
FE 개발자가 되고 싶은 짱잼이
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!