Potayto, Potahto
April 17, 2016So 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?
Implementation #1
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 RatingList
. A
very straight-forward implementation. But it could be improved.
Implementation #2
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.
The 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 ratingonChangeBegin
: When the user hovers over a star in the widgetonChangeAbort
: When the user hovers out of a star
The RatingWidgetContainer
is the “Container” for the RatingWidget
. It has an
internal UI state to keep a track of the hovered star. The handleChangeBegin
and the handleChangeAbort
are passed as the respective callbacks to the
RatingWidget
.
This implementation is better than the first one. Refactors components well and keeps stuff simple too.
Implementation #3
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.
The #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
instance, the #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
of the #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.
Fin.
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!