DEV Community

Cover image for Building a Custom Star Rating Component in React Native with Sliding and Press Interactions
Md Yousuf sheikh
Md Yousuf sheikh

Posted on

Building a Custom Star Rating Component in React Native with Sliding and Press Interactions

In mobile applications, interactive UI components like star rating systems significantly enhance user experience. In this blog post, we'll walk through building a custom star rating component in React Native that supports both sliding and press interactions for setting ratings. This component will also provide immediate visual feedback, enhancing the user interface and user engagement.

Prerequisites

Before we begin, ensure you have the following set up:

  • React Native development environment
  • Basic knowledge of React Native and TypeScript

Creating the Custom Star Rating Component

Let's break down the implementation of the CustomRatingStar component.

1. Setting Up the Component

We start by creating a functional component CustomRatingStar that accepts props like totalStars, initialRating, and onRate. The component maintains two state variables: rating for the final rating and hoverRating for the intermediate rating during sliding interactions.

import React, { useState, useRef } from "react";
import {
  StyleSheet,
  View,
  PanResponder,
  GestureResponderEvent,
  TouchableWithoutFeedback,
} from "react-native";
import { StartIcon } from "src/NativeBaseIcon";
import resSize from "@theme/resSize";

interface RatingProps {
  totalStars?: number;
  initialRating?: number;
  onRate?: (rating: number) => void;
}

const CustomRatingStar: React.FC<RatingProps> = ({
  totalStars = 5,
  initialRating = 0,
  onRate,
}) => {
  const [rating, setRating] = useState(initialRating);
  const [hoverRating, setHoverRating] = useState(initialRating);
  const containerRef = useRef<View | null>(null);
Enter fullscreen mode Exit fullscreen mode

2. Handling Sliding and Press Interactions

To handle sliding interactions, we use the PanResponder API. This API tracks gestures, allowing us to update the hoverRating as the user slides their finger across the stars. We also handle press interactions by calculating the rating based on the touch position relative to the container.

  const updateRating = (positionX: number, width: number, isFinal: boolean = false) => {
    const starWidth = width / totalStars;
    let newRating = Math.ceil(positionX / starWidth);
    newRating = Math.max(1, Math.min(newRating, totalStars));

    if (isFinal) {
      setRating(newRating);
      if (onRate) {
        onRate(newRating);
      }
    } else {
      setHoverRating(newRating);
    }
  };

  const panResponder = useRef(
    PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onMoveShouldSetPanResponder: () => true,
      onPanResponderMove: (_, gestureState) => {
        if (containerRef.current) {
          containerRef.current.measure((_, __, width, ___, pageX) => {
            const positionX = gestureState.moveX - pageX;
            updateRating(positionX, width);
          });
        }
      },
      onPanResponderRelease: (_, gestureState) => {
        if (containerRef.current) {
          containerRef.current.measure((_, __, width, ___, pageX) => {
            const positionX = gestureState.moveX - pageX;
            updateRating(positionX, width, true);
          });
        }
      },
    })
  ).current;

  const handlePress = (event: GestureResponderEvent) => {
    event.persist();
    if (containerRef.current) {
      containerRef.current.measure((_, __, width, ___, pageX) => {
        if (event.nativeEvent && width) {
          const positionX = event.nativeEvent.pageX - pageX;
          updateRating(positionX, width, true);
        }
      });
    }
  };
Enter fullscreen mode Exit fullscreen mode

3. Rendering the Stars

We use a loop to render the stars, and the color of each star is determined by comparing the current index with the hoverRating.

  return (
    <TouchableWithoutFeedback onPress={handlePress}>
      <View>
        <View
          ref={containerRef}
          style={styles.container}
          {...panResponder.panHandlers}
        >
          {Array.from({ length: totalStars }, (_, index) => (
            <StartIcon
              key={index}
              size={resSize(35)}
              color={index < hoverRating ? "#FF9900" : "#FFFFFF"}
            />
          ))}
        </View>
      </View>
    </TouchableWithoutFeedback>
  );
};
Enter fullscreen mode Exit fullscreen mode

4. Adding Styles

Finally, we define some basic styles for the component.

const styles = StyleSheet.create({
  container: {
    flexDirection: "row",
    justifyContent: "center",
    alignItems: "center",
    gap: 4,
  },
  ratingText: {
    marginTop: 10,
    textAlign: "center",
    fontSize: resSize(18),
    color: "#FF9900",
  },
});

export default CustomRatingStar;
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this tutorial, we built a custom star rating component in React Native that supports both sliding and press interactions. By utilizing React Native's PanResponder and state management, we provided a smooth and responsive user experience. This component can be a valuable addition to any mobile application requiring user feedback through a rating system.

Feel free to customize this component further to fit your application's needs. Happy coding!

Top comments (0)