React,Tailwindを使って、星で評価を投稿するUIを実現する機会があり、
どう実現するか悩み検索するも、なかなか記事が見つからなかったので、私が実際に書いたコードを元に今回記事を書いてみました。
使っている技術は
| 技術 | バージョン |
|---|---|
| React.js | 17.0.38 |
| Tailwind CSS | 3.0.13 |
| TypeScript | 4.5.4 |
です。完成形のイメージは以下のようなものです。
星アイコンの実装
まずは星のアイコンを実装します。
ここでは、selectedがTrueなら黄色、selectedがFalseならばグレーになるようにします。
以下はReactIconというnpmを利用していますが、SVGアイコンで実装してもそれほど変わりません。
import React, { VFC } from "react";
import { AiFillStar } from "react-icons/ai";
type PropsType = {
selected: boolean;
size?: string;
};
export const ColorizeStarIcon: VFC<PropsType> = ({
selected,
size,
}: PropsType) => {
const color = selected ? "text-yellow-400" : "text-gray-100";
const hoverColor = selected ? "text-gray-100" : "text-yellow-400";
return (
<AiFillStar
icon="fillStar"
color={color}
className={`hover:${hoverColor}`}
size={size}
/>
);
};
このアイコンのポイントは、
- テキストの色を 選択済みの場合は黄色、選択されていなければグレーにする
- Hoverされたときのテキストの色は、選択済みの場合はホバーされるとグレーになる、選択されていなければホバーされると黄色になる
という この部分です。
const color = selected ? "text-yellow-400" : "text-gray-100";
const hoverColor = selected ? "text-gray-100" : "text-yellow-400";
星を並べる
続いて上で作った星を並べていきます。
- 星の数(count)
- 選択された星の数(value)
- 星を変更できるか(readonly)
- 星の変更時の関数(onChange)
をpropsで渡されるコンポーネントを作成します。
以下、コードをしまします。詳しくは下で解説します。
import React, { VFC, useState } from "react";
import { ColorizeStarIcon } from "~/components/atoms";
type PropsType = {
count: number;
value: number;
readonly?: boolean;
size?: string;
onChange?: (value: number) => void;
};
export const StarRatingField: VFC<PropsType> = ({
count,
value,
readonly = false,
size,
onChange,
}: PropsType) => {
const [hover, setHover] = useState(-1); // Hover時にも色を変更できるように、hoverされている星の数を管理します
const onClick = (starNumber: number) => {
if (readonly) return;
onChange && onChange(starNumber);
setHover(-1);
};
const onHover = (starNumber: number) => {
if (readonly) return;
setHover(starNumber);
};
const onMouseLeave = () => {
if (readonly) return;
setHover(-1);
};
const currentValue = hover >= 0 ? hover : value;
return (
<>
<ul className="flex items-center space-x-1">
{[...Array(count)] // countの数だけの要素を持った配列を作成
.map((_, i) => i + 1)
.map((starNumber) => (
<li
title={`${starNumber} star`}
key={starNumber}
onClick={() => onClick(starNumber)}
onMouseOver={() => onHover(starNumber)}
onMouseLeave={() => onMouseLeave()}
className={readonly ? "" : "cursor-pointer"}
>
<ColorizeStarIcon
selected={starNumber <= currentValue} // currentValue以下のindexは、selectedをTrueにする
size={size}
/>
</li>
))}
</ul>
</>
);
};
ポイントは、
- 星(count)の数だけ配列を作る: [...Array(count)]
- 選択された星の数(value)の数だけ、先ほど作った ColorizeStarIconコンポーネントのselectedをTrueにします
- Hover時にも色を変更できるように、hoverされている星の数を管理します
- マウスがhoverされたときには、そのhoverされた星のindex+1番にhoverの数を更新します
- マウスのhoverが外れたとき と クリックされたとき には、そのhoverの数を-1に更新します
ざっくりですが、上記のようにすると、以下のような星の評価をできるUIを実装できます。
必要になったときにご参考ください。



