별점 평점 은 리뷰 기능을 만들 때 빠질 수 없는 요소이다.
현재 진행 중인 프로젝트의 리뷰 작성 페이지를 만들면서 별점 평점 기능을 만들게 되었다.
요새 Chakra UI 처럼, 외부 UI 라이브러리가 매우 잘 되어있기 때문에 자주 쓰이는 UI 컴포넌트들은 대부분 라이브러리를 사용해 만들고 나는 로직에 더 집중하는 방식으로 개발을 진행했었다.
하지만 아쉽게도 (?) 자주 쓰는 라이브러리에서는 별점 컴포넌트가 제공되지 않았고
로직을 생각해보니 그리 어렵지 않은 것 같아서 직접 만들어보았다.
(찾아보니 있긴 했다.)
https://www.npmjs.com/package/rc-rate
rc-rate
React Star Rate Component. Latest version: 2.12.0, last published: 7 months ago. Start using rc-rate in your project by running `npm i rc-rate`. There are 410 other projects in the npm registry using rc-rate.
www.npmjs.com
설계
- 값
- 초기값 : 5점
- 최소값 : 1점
- 최대값 : 5점
- 주요 로직
- 별을 클릭 시, 그보다 상위 점수를 표시하는 별은 비활성화되어야 하고 하위 점수를 표시하는 별은 활성화되어야 한다.
- 각 별마다 useState를 사용해 활성화 상태를 관리해준다.
- 별 이미지 교체 방식
- svg 파일을 2개 사용한다.
- ic-star-on.svg
- ic-star-off.svg
- 객체를 이용해 true와 on, false와 off를 각각 치환해준다.
- svg 파일을 2개 사용한다.
초기 코드
import { useEffect, useState } from "react";
const Star = () => {
const [rateScore, setRateScore] = useState(0);
const [star1, setStar1] = useState(false);
const [star2, setStar2] = useState(false);
const [star3, setStar3] = useState(false);
const [star4, setStar4] = useState(false);
const [star5, setStar5] = useState(false);
const icList: { [key: string]: string } = {
true: "on",
false: "off",
};
const clickStar = (starScore: number) => {
if (starScore === 1) {
setRateScore(1);
setStar1(true);
setStar2(false);
setStar3(false);
setStar4(false);
setStar5(false);
} else if (starScore === 2) {
setRateScore(2);
setStar1(true);
setStar2(true);
setStar3(false);
setStar4(false);
setStar5(false);
} else if (starScore === 3) {
setRateScore(3);
setStar1(true);
setStar2(true);
setStar3(true);
setStar4(false);
setStar5(false);
} else if (starScore === 4) {
setRateScore(4);
setStar1(true);
setStar2(true);
setStar3(true);
setStar4(true);
setStar5(false);
} else if (starScore === 5) {
setRateScore(5);
setStar1(true);
setStar2(true);
setStar3(true);
setStar4(true);
setStar5(true);
}
};
return (
<MainWrapper>
<StarWrapper>
<img
onClick={() => clickStar(1)}
className="star"
src={`/assets/svg/ic-star-${icList[star1.toString()]}.svg`}
alt="star"
/>
<img
onClick={() => clickStar(2)}
className="star"
src={`/assets/svg/ic-star-${icList[star2.toString()]}.svg`}
alt="star"
/>
<img
onClick={() => clickStar(3)}
className="star"
src={`/assets/svg/ic-star-${icList[star3.toString()]}.svg`}
alt="star"
/>
<img
onClick={() => clickStar(4)}
className="star"
src={`/assets/svg/ic-star-${icList[star4.toString()]}.svg`}
alt="star"
/>
<img
onClick={() => clickStar(5)}
className="star"
src={`/assets/svg/ic-star-${icList[star5.toString()]}.svg`}
alt="star"
/>
</StarWrapper>
</MainWrapper>
);
};
export default Star;
리팩터링
개발일지를 정리하며 코드를 옮겨두고 보니, 별점을 관리하는 clickStar 함수의 길이가 너무 길었다. 비슷한 형태의 로직이 의미 없이 반복되고 있었기 때문이다. 이를 개선하기 위해 별점 이미지에서 클릭 이벤트 발생 시 id 값을 전달하고, 별점을 관리하는 함수들을 배열에 담아 이를 순회하게 하였다. 클릭된 별의 id 이하의 별은 활성화, 초과의 별은 비활성화하게 하였다. 이를 통해 불필요하게 길었던 함수를 더 짧고 명확하게 개선할 수 있었다.
const clickStar = (e: any) => {
const starID = e.target.id;
setRateScore(Number(starID));
const funcList = [setStar1, setStar2, setStar3, setStar4, setStar5];
for (let i = 0; i < 5; i++) {
if (i < +starID) funcList[i](true);
else funcList[i](false);
}
};
완성 화면
'Library-Framework > React' 카테고리의 다른 글
React 컴포넌트를 어떻게 만들어야 할까? (0) | 2024.01.20 |
---|---|
[React] 폰트 파일 추가하기 (0) | 2023.04.08 |
[React] Google Map Polygon 바로 띄우기 (0) | 2023.04.03 |
[React] Google Map default zoom 바로 안 먹힘 (0) | 2023.04.03 |
[React] React에서 Google Map API 사용하기 & Polygon 그리기 (0) | 2023.02.19 |