Five Star Rating System

Create an interactive rating collection component.


Environment

Use the environment you are most comfortable with. I recommend using create-react-app to create a local version of the project so that you can inspect the DOM easily from your browser when debugging. Alternatively, you can work from a blank React template in CodeSandbox. Starter code is provided after the problem specification.

Specification

Write a functional component that allows a user to enter ratings on the five-star system. The component should initially display five empty stars in a row. When the user hovers over a star, that star as well as all the stars that come before it in the row should become filled (both the empty star and filled star image URLs are provided in the started code below). When the user stops hovering over the star, the component should return to its starting state. Further, if a user clicks on a star, then the component should continue to display that rating even after the user stops hovering; the component should reset only after the user hovers on a star that comes before the clicked star. If the user hovers over later stars, the component should fill those stars but should still display the clicked rating after the hover ends.

The component’s initial state
The component’s initial state
The component with three stars highlighted
The component with three stars highlighted

Starter Code

Here is some code to get you started:

import React from "react";
import { useState } from "react";

const EMPTY_STAR =
  "https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/Five-pointed_star.svg/1088px-Five-pointed_star.svg.png";
const FULL_STAR =
  "https://upload.wikimedia.org/wikipedia/commons/thumb/2/29/Gold_Star.svg/1200px-Gold_Star.svg.png";

function App() {
  return <StarRating />;
}

const StarRating = () => {
  // YOUR CODE HERE
};

export default App;

Source URLs for the images of empty and filled stars are included as constants. Feel free to divide the task into multiple subcomponents.

Solution

import React from "react";
import { useState } from "react";

const EMPTY_STAR =
  "https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/Five-pointed_star.svg/1088px-Five-pointed_star.svg.png";
const FULL_STAR =
  "https://upload.wikimedia.org/wikipedia/commons/thumb/2/29/Gold_Star.svg/1200px-Gold_Star.svg.png";

function App() {
  return <StarRating />;
}

const StarRating = () => {
  const starIds = [1, 2, 3, 4, 5];
  const [hovered, setHovered] = useState(0);
  const [clicked, setClicked] = useState(0);

  const getImg = (id) => {
    return hovered >= id || clicked >= id ? FULL_STAR : EMPTY_STAR;
  };

  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        padding: 20,
      }}
    >
      {starIds.map((id) => (
        <img
          src={getImg(id)}
          onMouseEnter={() => {
            setHovered(id);
            if (id < clicked) setClicked(0);
          }}
          onClick={() => setClicked(id)}
          onMouseOut={() => setHovered(0)}
          width={60}
          height={60}
        />
      ))}
    </div>
  );
};

export default App;

We first define an array of star IDs; they are numbered one to five from left to right. We then create state variables to store the IDs of the currently hovered and clicked stars; in the component’s initial state, these values are zero. We define a helper function in the component that returns the image source for a particular star based on its ID and the hovered and clicked IDs. If at least one of these state variables is greater than or equal to the ID in question, the star should appear filled. The remaining logic that governs the component is in the callback functions passed to onMouseEnter, onClick, and onMouseOut. We map over the starIds array and render an image with the appropriate callbacks based on its ID. Importantly, when the user hovers a star that appears before the clicked star, we reset the component.


useState