0. 버그 현황
- 이틀째 시달리는 에러.....어떻게 해결하면 좋을지 모르겠음
- redux-toolkit 의 createAsyncThunk 에서 react-query 로 장바구니 항목을 불러오는 페이지 리팩토링 하는 중 고난이도의 문제가 생김
- 장바구니 정보를 가져오는 API 를 불러오고 거기서 product_id 값을 반환 받으면 그 product_id 값을 이용해서 상세 상품 정보를 불러오는 API 를 통신해야했다.
장바구니 불러오는 API 에서 product_id 를 빼오기 위해서 결국 반복문을 쓸 수 밖에 없었다.
// 장바구니 불러오는 API 응답값
{
"count": Int,
"next": String,
"previous": String,
"results": [
{
"my_cart": Int, // 카트 고유번호
"cart_item_id": Int, // cartItem의 고유번호
"product_id": Int, // 상품 아이디
"quantity": Int // 장바구니에 담긴 상품의 개수
}
]
}
- 그래서 리덕스 툴킷을 사용할 때는 반복문 안에 API 호출하는 코드를 넣었음
// 장바구니 정보 가져오기
useEffect(() => {
if (TOKEN) {
dispatch(fetchGetCartList(TOKEN));
}
}, [TOKEN, dispatch]);
// 상품 상세 정보 가져오기
useEffect(() => {
const getProductDetails = async () => {
for (const cartItem of cartItems) {
// 이미 상품 상세 정보를 가져온 경우는 제외
if (cartItem.item) {
continue;
}
dispatch(fetchGetProductDetail(cartItem.product_id));
}
};
if (cartItems.length > 0) {
getProductDetails();
}
}, [cartItems, dispatch]);
if (cartStatus === "loading") {
return <Spinner />;
}
- 하지만, react-query 는 Hook 이기 때문에 반복문안에서 호출할 수 없었음. 왜냐! 훅은 최상단에서 호출해야하기 때문이다.
const useFetchCartItems = (token: string) => {
const { data: cartList } = useFetchCartList(token);
//각 항목의 product_id 로 제품 상세 정보를 가져온다.
const productDetails = cartList.results.map((item: CartType) => {
return useFetchProductDetail(item.product_id);
});
let cartItems = cartList.results.map((item: CartType, index: number) => {
return { ...item, productDetail: productDetails[index].data };
});
return { cartItems };
};
export default useFetchCartItems;
🔸에러메세지
ERROR
[eslint] src\hooks\queries\useFetchCartItems.tsx Line 10:12: React Hook "useFetchProductDetail" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks Search for the keywords to learn more about each error.
1. 해결과정
- 초반에는 react-query가 훅이니까 react-query 없이 하면 문제가 해결된다.
import { useState, useEffect } from "react";
import productAPI from "../../API/productAPI";
import { CartType } from "../../types/Cart.type";
import useFetchCartList from "./useFetchCartList";
const useFetchCartItems = (token: string) => {
const { data: cartList } = useFetchCartList(token);
const [cartItems, setCartItems] = useState<any[]>([]);
useEffect(() => {
if (cartList && cartList.results) {
// map을 통해 각 제품의 상세 정보를 가져오는 Promise의 배열을 만듦.
const productDetailsPromises = cartList.results.map((item: CartType) => {
return productAPI.fetchProductDetail(item.product_id); // [Promise] 반환
});
// 모든 Promise가 완료되면 결과 배열을 가져와서 cartItems 상태를 설정.
Promise.all(productDetailsPromises).then((productDetails) => {
const updatedCartItems = cartList.results.map(
(item: CartType, index: number) => {
return { ...item, productDetail: productDetails[index] };
}
);
setCartItems(updatedCartItems);
});
}
}, [cartList]);
return { cartItems };
};
export default useFetchCartItems;
useQueries 로 문제 해결
- 하지만, react-query 를 사용했으니 react-query 로 해결해야되지 않을까!!
useQueries로 해결했다. 여러개의 useQuery 를 동시에 수행해야하는데 렌더링이 거듭되는 사이사이에 계속 쿼리가 수행되어야 한다면 쿼리를 수행하는 로직이 hook 규칙에 어긋날수도 있을때는 useQueries를 사용한다.
아래와 같이 useQueries 를 사용하면 undefined 가 useQueries의 반환값으로 나온다고 에러가 뜨는데..
const useFetchCartItems = (token: string) => {
// CartList 가져오기
const { data: cartList } = useFetchCartList(token);
// console.log(cartList);
const productIds = cartList.results.map((item: any) => {
return item.product_id;
});
console.log("Product IDs:", productIds);
const queryInput = productIds.map((productId: string) => {
return {
queryKey: ["cartProductDetail", productId],
queryFn: () => productAPI.fetchProductDetail(productId),
};
});
console.log("Query Input:", queryInput);
const cartItemsResponses = useQueries(queryInput);
console.log("윗부분이 오류임");
// 결과를 기반으로 cartItems 생성
const cartItems = cartList?.results?.map((item: any, index: any) => ({
...item,
productDetail: cartItemsResponses[index]?.data,
}));
return { cartItems };
};
export default useFetchCartItems;
분명 다 잘되는데
const cartItemsResponses = useQueries(queryInput); 이부분에서 계속 오류가 났다..
뭘까 뭘까 3일째 고민하다가 버전차이인걸 알았다. 하...허탈하다
const cartItemsResponses = useQueries({ queries: queryInput });
- useQueries v3 에서 v4로 변경되면서 queries 프로퍼티를 가진 객체를 넘겨주는 것이었다
// v3
const queryResults = useQueries(
heroIds.map((id) => ({
queryKey: ["super-hero", id],
queryFn: () => getSuperHero(id),
}))
);
/*
const queryResults = useQueries(
{
queryKey: ['super-hero', 1],
queryFn: () => fetchSuperHero(1)
},
{
queryKey: ['super-hero', 2],
queryFn: () => fetchSuperHero(2)
},
// ...
);
*/
// v4
const queryResults = useQueries({
queries: [
{
queryKey: ["super-hero", 1],
queryFn: () => fetchSuperHero(1),
staleTime: Infinity, // 다음과 같이 option 추가 가능
},
{
queryKey: ["super-hero", 2],
queryFn: () => fetchSuperHero(2),
staleTime: 0,
},
// ...
],
});
3일동안 고민하던거 끝 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ 문서를 잘보자..ㅠㅠ
'프로젝트 이모저모 > HoduMarket 프로젝트' 카테고리의 다른 글
오픈마켓 프로젝트) 이미지 최적화 (0) | 2023.08.31 |
---|---|
오픈마켓 버그) 장바구니에 상품을 담을 경우 TotalPrice 컴포넌트 업데이트 안됨 (0) | 2023.08.18 |
타입스크립트 에러) useParams undefined|string 반환 아직 미해결 (0) | 2023.08.08 |
오픈마켓 버그) 페이지네이션 다음 버튼 클릭하면 화면 렌더링 되지 않음 (0) | 2023.07.25 |
오픈마켓 프로젝트) createAsyncThunk 에서 React-Query로 리팩토링 (0) | 2023.07.18 |