Typically in React when you want to render items based on data in an array,
you use map
as in something like this:
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
);
This works fine most of the time — but sometimes you don't have an array to start with. I was recently reading a post by Josh Comeau in which he provides a few solutions. I've run into this problem from time-to-time, and wanted to explore the options.
Say you want to display a gold star icon based on rating. The rating prop will be a number from 0 to 5 and you want to show 0 to 5 stars. One way to do this is to make an array and render the array.
const Stars = ({ rating }) => {
const stars = [];
for (let i = 0; i < rating; i++) {
stars.push(<StarIcon />);
}
return <div className="star-wrapper">{stars}</div>;
};
He prefers to create a range utility, then use it with map
:
function range(start, end, step = 1) {
if (end === "undefined") {
end = start;
start = 0;
}
const arr = [];
for (let i = start; i < end; i += step) {
arr.push(i);
}
return arr;
}
// in a Stars comnponent where `rating` is a number from 0 to 5
return (
<div className="star-wrapper">
{range(rating).map((num) => (
<StarIcon key={num} />
))}
</div>
);
I quite like this solution that leverages a lesser-known aspect of Array.from, which is that it takes two arguments: an iterable (e.g. an array or array-like object) and a function that maps over the iterable.
const arrayOfRandomNumbers = Array.from({ length: 10 }, () =>
Math.floor(Math.random() * 10)
);
// returns an array like this: [2, 3, 9, 3, 1, 7, 0, 4, 3, 5]
This approach can work with the range method, too:
const range = (start, end, step = 1) => Array.from(
{ length: (end - start) / step },
(_, i) => start + i * step;
)