So these days I am mentoring my team on React. As an exercise I asked them to write a rating widget. And I was blown seeing the different approaches they took. Almost each implementation was different.
This was the expected way the widget should work. Nothing special, usual rating widget you'd see anywhere.
So let's get digging into the implementations shall we?
So, this is a very straighforward implementation. A state
current to track the
currently set rating and another
supposed to track the rating when user is
changing it. The
rating state is used to map over each star. An unnecessary
one. Could have just used a
for loop instead.
handleSetRating sets the
current state when a star is clicked.
handleSupposeRating changes the
supposed as the user hovers around the stars.
handleRevertRating resets the
supposed to the last set
current values to revert when the user just aborts
hovering over the widget. Pretty simple.
All the heavy lifting is done by the
RatingList component. While the
RatingWidget acts more like a "Container" component to the
very straight-forward implementation. But it could be improved.
This one has 3 set of components. It begins with a simple
Star component that
is either active or inactive depending on its
isActive property. It is a pure
components, very nice.
RatingWidget is also a pure components and it just displays the ratings
based on the
size and the
rating props. Has a very simple implementation
again. It uses the
Star component to simply a lot of the output. It also gets
3 callbacks, one for each:
onRatingChange: When the user clicks a star to pick a rating
onChangeBegin: When the user hovers over a star in the widget
onChangeAbort: When the user hovers out of a star
RatingWidgetContainer is the "Container" for the
RatingWidget. It has an
internal UI state to keep a track of the hovered star. The
handleChangeAbort are passed as the respective callbacks to the
This implementation is better than the first one. Refactors components well and keeps stuff simple too.
This one is the most interesting one. There is just one component and it just
renders the stars depending on the current rating. So nothing special here. But
how does it do the hovering changes? It kind of hands of this task to CSS. The
#rating .star:hover ~ .star and
#rating:hover .star are of interest to us.
#rating .star:hover ~ .star uses the general sibling selector to render
all the following stars grey. So pretty smart here. But we also want to display
all the other stars golden. So it uses the
#rating:hover .star to accomplish
it. Very cool indeed.
This is by far the best implementation. The ony thing is you need to be careful
with the styles. A lot depends on the way the markup is laid out and stlyed. For
#rating cannot be a block level element or if you hover on it
where there are no stars, all the stars will get active. Also, the specificity
#rating .star:hover ~ .star needs to be higher than
#rating:hover .star or it breaks too. In short, the JS gets simpler, but the
CSS get a beating.
It was interesting to see and critique the different implementations when I was them first. Also I am pretty sure there could be some more interesting ways to implement it. Also, I might have missed some points here. Feel free to post your thoughts below in the comments or propse an even better solution. Cheers!