Photo by RetroSupply on Unsplash
Day 25 of 100days of code
Hello and welcome to day 25 of my challenge!β€π₯
Building a reusable star rating component.
I'm going to build a reusable StarRating
component for the UsePopcorn
app. Initially, I'll develop it outside the app as a separate component. Later, I'll integrate it into the UsePopcorn
app. First, I'll create a StarRating
component within our components
folder and render it in Main.jsx
instead of the App
component. However, this setup is temporary. ππ
To dynamically generate the star elements, enabling the usage of any number of stars, we enter JavaScript mode on line 5. Utilizing the Array.from
method, we create an array with a length of 5. The (_, i) => ...
function serves as a callback function for Array.from
. It accepts two parameters, but only utilizes the second one (i)
, representing the index of the current element being created.ππ
ππI received a warning about the key props, so I'll fix that shortly.
The outputππ
I've added some basic inline styles here.ππ
Now, as a user of this component, for example, if I wanted to pass in a maxRating
as props, it would look like thisπ
The result remains the sameππ
Now, I'll destructure the maxRating
prop and use it in the StarRating
component. { length: maxRating }
specifies the length of the array to be created. It's an object literal with a single property, length
, whose value is set to maxRating
. By doing this, it essentially tells Array.from
to create an array with a specific length determined by the maxRating
prop.ππ
The output nowππ
Now, what if the user decides to use the StarRating
component without passing any props?π€π€
Here, we need to set a default value for the maxRating
, and we can accomplish this by leveraging the power of destructuring.ππ
Creating the stars and reacting to a click event
To create the Star
component, I've included some basic styles and an SVG for the starππ
Next, I use the Star
component within the StarRating
component instead of the span
, and I also pass the key
prop accordingly.π
Looks like this nowππ
I'm going to improve the accessibility of the span
element in the Star
component, and this is not specifically related to Reactππ
Now to react to a click event π€:
It sounds like we'll need to utilize some stateπ€πͺ
Here, numRating
is a state variable created using the useState
hook in React. Its initial state is set to 0 using useState(0)
. Then, we use it in the JSX. This JSX snippet renders a <p>
element to display the current value of numRating
. The value of numRating
is displayed inside the paragraph element. If numRating
is falsy (such as 0), an empty string "" is displayed instead. Additionally, an onRate
prop is added to the Star
component. This prop specifies the function to be called when the Star
component is clicked. In this case, an arrow function is used to create an anonymous function that calls setNumRating(i + 1)
.πππ
Now, to use the onRate
prop in the Star
componentππ
I rated this differently, and the values of the stars showed different ratingsππ
Now, to conditionally render the filled star or an empty star:
I've added a prop called full
. If this prop is true, the filled star is displayed; otherwise, the empty star is displayed. Since the full
prop hasn't been passed into the StarRating
component, the empty star is rendered by default. The code conditionally renders different SVG elements based on the value of the full
prop: when full
is true, the SVG for a filled star is rendered, and when full
is false or undefined, the SVG for an empty star is rendered.ππ
How do we decide whether a star is filled or not:
Now, we create a condition that will evaluate to either true or false. It compares the current rating (numRating
) with the index of the star being rendered (i + 1
). If the current rating is greater than or equal to the index of the star being rendered, it means that the star should be filled to represent a selected rating. Otherwise, it remains empty to represent an unselected rating. The full
prop in this context is used to determine whether the star should be displayed as filled or empty, based on the current rating (numRating
) and the index of the star being rendered (i
).ππ
Handling hover events
Now, to add a hover event, we'll create a temporary rating for the number of stars currently being hovered over. It's important to note that this is just temporary, as the onRate
value still remains constant.
So, we'll create a new hoverRating
state for this purpose.ππ
Next, I'll create two props onHoverIn
and onHoverOut
to handle the mouseEnter
and mouseLeave
events.ππ
Now, I'll destructure these props in the Star
component and add the event listener. It's worth noting that we haven't used the state variable in the JSX
just yet.ππ
Now, I'm going to use this in the JSX
and also conditionally render the filled star or empty star.ππ
Props as a component API
When we build reusable components like this, we should carefully consider which props the component needs. As a component creator, we must think about the component's potential consumers, whether it's ourselves in this case or members of our team if we're collaborating. As the creator, we define the props that the component can receive. The consumer then utilizes the component by specifying values for these props. We can think of the component props as the public API of the component. Just like how a public API defines how external systems or users can interact with a software system, component props define how other components or parts of the application can interact with a reusable component. The props serve as the contract between the component and its consumers, outlining what functionalities are available and how they can be accessed or customized.
Improving reusability with props
Color and Size :
Let's begin with the color
and size
propsππ
Specify default values for the color
and size
propsππ
Now, let's use these props into our text styleππ
We'll also pass them as props and utilize them in the Star
component for the star style ππ
Also, we'll use them for the fill colors in the Star
componentππ
See resultππ
To modify some of these default properties, I'll pass in additional propsππ
Messages:
This prop is an array of strings, where each string represents a message associated with a particular rating.ππ
Setting a default valueππ
Here ππ, If
messages.length === maxRating
, it indicates that there is a message provided for each possible rating. In such a scenario, the message corresponding to the selected rating is displayed using the index of the rating (adjusted for zero-based indexing).However, if the length of
messages
doesn't matchmaxRating
, or ifmessages
is not provided, the displayed text defaults to showing the currently hovered rating (hoverRating
) if available. Otherwise, it displays the current rating (numRating
) if there's no hover interaction.πππ
Thank you for readingβ€πͺ...