const useFetch = (endpoint, options = {}) => {
const { isMocked = false, method = "get", params = {}, body = {} } = options;
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchData = async () => {
console.log(
`endpoint: ${endpoint} method: ${method} params: ${JSON.stringify(params)} body: ${JSON.stringify(body)}`,
);
try {
setLoading(true);
const service = isMocked ? mockService : apiService;
let response;
switch (method) {
case "GET":
response = await service.get(endpoint, params);
break;
case "POST":
response = await service.post(endpoint, body);
break;
case "PUT":
response = await service.put(endpoint, body);
break;
case "DELETE":
response = await service.delete(endpoint, params);
break;
default:
response = await service.get(endpoint, params);
}
setData(response);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [endpoint, method, JSON.stringify(params), JSON.stringify(body)]);
return { data, loading, error, fetchData };
};
다음의 커스텀 훅을 만들고 사이트를 테스트를 했다.
그런데 /lectures/categories
부분에서 2번 호출되는 경우가 있었다.
먼저 lectures/categories
를 호출하는 컴포넌트가 어디인지부터 확인해야했다.
const Home = () => {
const [selectedCategory, setSelectedCategory] = useState(0);
const {
data: topLanguages,
loading: topLanguagesLoading,
error: topLanguagesError,
} = useFetch(ENDPOINTS.TOP_LANGUAGES, { isMocked: true, method: "GET" });
const {
data: lectures,
loading: lecturesLoading,
error: lecturesError,
fetchData: fetchLectures,
} = useFetch(ENDPOINTS.LECTURES, {
isMocked: false,
method: "get",
params: { category: categoryKeywords[selectedCategory].eng_category },
});
useEffect(() => {
fetchLectures();
}, [selectedCategory]);
if (topLanguagesLoading || lecturesLoading) return <Loading />;
if (topLanguagesError || lecturesError) return <Error />;
return <컴포넌트 />;
};
selectedCategory
를 통해 카테고리를 선택하고, 그 값이 바뀌면 fetchLectures를 한다.
useFetch(ENDPOINTS.LECTURES)
를 해야하는 경우는 다음과 같다.
Home
컴포넌트에 들어갈 때selectedCategory
가 변경될 때위의 코드는 useFetch
내부의 useEffect
를 통해 최초에 호출된다.
최초 Home
컴포넌트에 진입 시, 1번째 useFetch
가 작동한다.
그리고, selectedCategory
도 Home
컴포넌트에 진입 시 useEffect
에 의해 fetchLectures
를 한 번 더 호출하는 것이다.
Home
컴포넌트에 들어간 후 selectedCategory
를 바꾸면, 이때는 제대로 fetchLectures
를 한 번만 호출한다.
즉, 최초 Home
컴포넌트에 진입 시 fetch를 2회 호출한다.
Home
컴포넌트에 진입한 상태에서 selectedCategory
를 바꿀 땐 정상적으로 fetch를 1회 호출한다.
1번의 경우를 수정하면 되는 것이다.
useEffect
에는 의존성배열을 제공하기에 이 값이 변경될때마다 호출하도록 할 수 있다.
즉, useFetch
커스텀 훅에 의존성배열을 입맛대로 조정할 수 있는 기능을 추가하면 된다.
const useFetch = (endpoint, options = {}) => {
const {
isMocked = false,
method = "get",
params = {},
body = {},
dependencies = {},
} = options;
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchData();
}, [
isMocked,
endpoint,
method,
JSON.stringify(params),
JSON.stringify(body),
JSON.stringify(dependencies),
]);
const fetchData = async () => {
console.log(
`endpoint: ${endpoint} method: ${method} params: ${JSON.stringify(params)} body: ${JSON.stringify(body)} dependencies: ${JSON.stringify(dependencies)}`,
);
try {
setLoading(true);
const service = isMocked ? mockService : apiService;
let response;
switch (method) {
case "GET":
response = await service.get(endpoint, params);
break;
case "POST":
response = await service.post(endpoint, body);
break;
case "PUT":
response = await service.put(endpoint, body);
break;
case "DELETE":
response = await service.delete(endpoint, params);
break;
default:
response = await service.get(endpoint, params);
}
setData(response);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
return { data, loading, error, fetchData };
};
dependencies
라는 값을 추가한다.
const Home = () => {
const [selectedCategory, setSelectedCategory] = useState(0);
const {
data: topLanguages,
loading: topLanguagesLoading,
error: topLanguagesError,
} = useFetch(ENDPOINTS.TOP_LANGUAGES, { isMocked: true, method: "GET" });
const {
data: lectures,
loading: lecturesLoading,
error: lecturesError,
} = useFetch(ENDPOINTS.LECTURES, {
isMocked: false,
method: "get",
params: { category: categoryKeywords[selectedCategory].eng_category },
dependencies: { selectedCategory: selectedCategory },
});
if (topLanguagesLoading || lecturesLoading) return <Loading />;
if (topLanguagesError || lecturesError) alert("에러가 발생했습니다.");
return <컴포넌트 />;
그리고 Home
컴포넌트에서는 useFetch
를 사용할때 dependencies
를 넘긴다.
이렇게 되면, dependencies
가 변경되면 1회면 호출을 하게 된다.
최종적으로 최초로 페이지에 접근해도 fetch를 1번만 하게 된다.