JD(회사) 상세페이지에 현재 상세정보, 리뷰(리뷰개수) 탭이 존재한다
현재 아래와 같이 구현해놓았다
- 리뷰 수가 9개 이하면 reviewCount, 9개 이상이면 9+ 보여주기
- 페이지 접속 시에는 상세정보 탭 활성화, 그리고 리뷰 탭을 클릭해야 리뷰 데이터 받아오기
- reviewCount 데이터가 아직 불러와지지 않았을 때는 9+로 잠시 뜨는 것
- 리뷰 수정 탭에서 리뷰를 추가 혹은 삭제했을 때 리뷰 탭에 리뷰 수가 즉각적으로 반영되지 않는 것
위의 문제는 둘 다 상태관리의 문제였다
프론트엔드에서는 다양한 상태관리법이 존재하는데 나는 state을 이용해서만 해보려고 한다
export function CategoryTab() {
const [value, setValue] = useState("1");
const [jdData, setJdData] = useState({});
const [reviewNum, setReviewNum] = useState(); // 리뷰 개수 state
const { id } = useParams();
const handleTabChange = (e, newValue) => {
setValue(newValue);
};
useEffect(() => {
(async () => {
const res = await getJdDetail(id);
setJdData(res);
setReviewNum(res.reviewCount); // api 응답 데이터.reviewCount를 저장
})();
}, [id, jdData.reviewCount]);
return (
<Box>
<TabContext value={value}>
<TabList
onChange={handleTabChange}
sx={{ pt: 2 }}
TabIndicatorProps={{ style: MainStyles.TabIndicator }}
>
<Tab label="상세 정보" value="1" sx={MainStyles.Tab} />
<Tab
label={`리뷰(${
jdData.reviewCount < 9 ? jdData.reviewCount : "9+"
})`}
value="2"
sx={MainStyles.Tab}
/>
</TabList>
<TabPanelItem value="1">
<TabForInfo jdData={jdData} />
</TabPanelItem>
<TabPanelItem value="2">
<TabForReview id={id} />
</TabPanelItem>
</TabContext>
</Box>
);
}
데이터 로딩 전에 "9+"가 잠깐 보이는 문제는 reviewNum 상태의 초기값 설정 때문에 발생하는 것이다
초기 상태가 undefined로 설정되어 있고, 조건부 렌더링에서 undefined < 9로 설정하면 undefined 이기 때문에 false가 되어 "9+"가 잠시 표시되는 것이다 ㅠㅠ
이 문제를 해결하기 위해 reviewNum의 초기값을 명시적으로 0으로 설정하거나, 데이터 로딩 상태를 나타내는 별도의 상태 값을 사용하여 로딩 중과 로딩 완료 상태를 구분해야한다!
다음과 같이 reviewNum의 초기값을 0으로 설정하면 로딩 되기 전 9+ 가 뜨는 것을 방지할 수 있다
const [reviewNum, setReviewNum] = useState(0);
(이 부분은 리팩토링 과정에서 리액트 쿼리로 대체할 것이다)
export function CategoryTab() {
const [value, setValue] = useState("1");
const [jdData, setJdData] = useState({});
const [reviewNum, setReviewNum] = useState(0);
const [isLoading, setIsLoading] = useState(true);
...
useEffect(() => {
(async () => {
setIsLoading(true); // 데이터 로딩 시작
const res = await getJdDetail(id);
setJdData(res);
setReviewNum(res.reviewCount);
setIsLoading(false); // 데이터 로딩 완료
})();
}, [id]);
return (
<Box>
<TabContext value={value}>
<TabList
onChange={handleTabChange}
sx={{ pt: 2 }}
TabIndicatorProps={{ style: MainStyles.TabIndicator }}
>
<Tab label="상세 정보" value="1" sx={MainStyles.Tab} />
<Tab
label={
isLoading
? "리뷰 로딩 중..."
: `리뷰(${reviewNum < 9 ? reviewNum : "9+"})`
}
value="2"
sx={MainStyles.Tab}
/>
</TabList>
이렇게 isLoading state을 추가하여 label 도 isLoading 의 조건을 추가하여 구현하였다
로딩상태일 때는 "리뷰 로딩 중..." 을 뜨도록 구현함으로써 더 친절한 UI가 되었다
리액트는 state 변경이 될때마다 재렌더링이 된다
위의 조건을 이용하여 reviewNum setter 함수를 prop으로 내려줌으로써 해결할 수 있었다
CategoryTab.tsx
<TabPanelItem value="2">
<TabForReview id={id} setReviewNum={setReviewNum} />
</TabPanelItem>
</TabContext>
TabForReview
export function TabForReview({ id, setReviewNum }) {
const { isOpen, openPopup, closePopup } = usePopup();
const loginState = useRecoilValue(isLoggedInState);
const [reviewData, setReviewData] = useState({ content: [], pageInfo: {} });
...
const addReviewAndUpdate = async (newReview) => {
await addReivew({ jdId: Number(id), content: newReview.content });
fetchReviewData(0);
alert("리뷰가 등록되었습니다");
setReviewNum((prev) => prev + 1);
closePopup();
};
const deleteReviewAndUpdate = async (reviewId) => {
await delReivew(reviewId);
fetchReviewData(0);
alert("리뷰가 정상적으로 삭제되었습니다");
setReviewNum((prev) => prev - 1);
};
이렇게 리뷰 추가, 리뷰 삭제 함수에서 reViewNum을 리액트 상에서 변경을 하면서 UI 적으로 동작이 잘 되도록 구현할 수 있었다