I am using React and an icon library where I found filled stars (FaStar) and empty stars (FaRegStar). I created a function where I pass a number (1-5) and depending on this value my function should return the appropriate number of filled stars.
const getStars = (rate) => {
if (rate <= 1) {
return (<>
<FaStar />
<FaRegStar />
<FaRegStar />
<FaRegStar />
<FaRegStar />
</>
)
}
else if (rate <= 2) {
return (<>
<FaStar />
<FaStar />
<FaRegStar />
<FaRegStar />
<FaRegStar />
</>
)
}
else if (rate <= 3) {
return (<>
<FaStar />
<FaStar />
<FaStar />
<FaRegStar />
<FaRegStar />
</>
)
}
else if (rate <= 4) {
return (<>
<FaStar />
<FaStar />
<FaStar />
<FaStar />
<FaRegStar />
</>
)
}
else if (rate <= 5) {
return (<>
<FaStar />
<FaStar />
<FaStar />
<FaStar />
<FaStar />
</>
)
}
}
While it works, it doesn't look good, do you have any ideas on how to return as many filled stars as the value provided?
You can build two arrays, then render those; be sure to handle key correctly:
const getStars = (rate) => {
const faStars = Array.from({length: rate}, (_, index) =>
<FaStar key={"star" + index} />
);
const faRegStars = Array.from({length: 5 - rate}, (_, index) =>
<FaRegStar key={"regstar" + index} />
);
return <>{faStars}{faRegStars}</>;
};
Live Example:
const {Fragment, useState, useEffect} = React;
const FaStar = () => <span>★</span>;
const FaRegStar = () => <span>☆</span>;
const getStars = (rate) => {
const faStars = Array.from({length: rate}, (_, index) =>
<FaStar key={"star" + index} />
);
const faRegStars = Array.from({length: 5 - rate}, (_, index) =>
<FaRegStar key={"regstar" + index} />
);
// Note: Stack Snippets don't support <> fragments,
// only the old verbose kind
return <Fragment>{faStars}{faRegStars}</Fragment>;
};
const Example = () => {
const [stars, setStars] = useState(1);
useEffect(() => {
const timer = setInterval(() => {
setStars(s => (s % 5) + 1);
}, 800);
return () => {
clearInterval(timer);
};
}, []);
return <div>{getStars(stars)}</div>;
};
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
I've used two arrays there, but you can do it with one array as well, as shown by moonwave99. Doesn't really matter either way in terms of the work that gets done (not that that matters with arrays this small anyway), but each approach suits a different mental model.
You can create two arrays, one for <FaStar/> and the other for <FaRegStar/>), using [...Array(rate)] and [...Array(5-rate)].
Then map both arrays using map function and return the corresponding component. Don't forget to add key={SOMETHING_UNIQUE} if you return a view inside map function.
const getStars = (rate) => (
<>
{
[...Array(rate)].map((item, index) => (
<FaStar key={index}/>
))
}
{
[...Array(5-rate)].map((item, index) => (
<FaRegStar key={index}/>
))
}
</>
)
With just one loop:
const getStars = (rate = 0, totStars = 5) => (
<>
{Array.from({ length: totStars },
(_, index) => (index >= rate ? <FaRegStar key={`regstar-${index}`} /> : <FaStar key={`star-${index}`}/>)
)}
</>
);