꼭 한번 구현해보고 싶었던 기능이었는데, 내가 맡은 역할이 아니여서 못했습니다. 그래도 한번 꼭 해보고 싶었기에, 쉬는 시간에 코드를 보고 공부하고, 혼자 만들어봤습니다.
만들면서 신기했던 부분은 on 으로 시작하는 이벤트가 생각보다 많다였습니다. 항상 많이 썼던 이벤트는 onClick, onChange 였는데, 처음으로 onMouseEnter,onMouseLeave를 사용해봤습니다.
아래 코드는 최상위 컴포넌트입니다.
// App.js
import React, { useState } from 'react';
import Stars from './RatingIcon';
function App() {
const [rating, setRating] = useState(0);
const [hoverRating, setHoverRating] = useState(0);
const onMouseEnter = (index: number) => setHoverRating(index);
// 마우스가 별 위에 올라가면 스테이트를 변경.
const onMouseLeave = () => setHoverRating(0);
// 마우스가 별 밖으로 나가면 스테이트를 0으로 변경.
const onSaveRating = (index: number) => setRating(index);
// 클릭시, 별 인덱스를 스테이트에 저장.
return (
<div>
{[1, 2, 3, 4, 5].map((idx) => {
return (
<Stars
index={idx}
rating={rating}
hoverRating={hoverRating}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onSaveRating={onSaveRating}
/>
);
})}
</div>
);
}
export default App;
아래코드는 Stars.tsx 입니다.
// Stars.tsx
import React, { useMemo } from 'react';
import StarIcon from './StarIcon';
interface Props {
index: number;
rating: number;
hoverRating: number;
onMouseEnter: (index: number) => void;
onMouseLeave: () => void;
onSaveRating: (index: number) => void;
}
const Stars = (props: Props) => {
const {
index,
rating,
hoverRating,
onMouseEnter,
onMouseLeave,
onSaveRating,
} = props;
const fillColor = useMemo(() => {
if (hoverRating >= index) {
return "#ffdb58"; // #ffdb58 === 노란색
} else if (!hoverRating && rating >= index) {
return "#ffdb58"; // #ffdb58 === 노란색
}
return "#dcdcdc"; // #dcdcdc === 회색
}, [rating, hoverRating, index]);
return (
<div
onMouseEnter={() => onMouseEnter(index)}
onMouseLeave={() => onMouseLeave()}
onClick={() => onSaveRating(index)}>
<StarIcon fillColor={fillColor} />
</div>
)
};
export default Stars;
마지막 컴포넌트는 최하위 컴포넌트이고, 별모양 svg입니다.
import React from 'react';
import { ReactComponent as StarSvg } from '../../images/review_star_full.svg';
// svg를 이미지 폴더 안에 있는 파일로 저장했다는 가정하에 import.
interface Props {
fill: string
}
const StarIcon = ({ fillColor = "#dcdcdc" }: Props) => {
return (
<div>
<StarSvg style={{ marginRight: '3px' }}fill={fillColor} />
</div>
)
}
export default StarIcon;
fill은 svg에서만 사용가능한 녀석입니다. MDN