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);
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);
}
});
}
};
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>
);
};
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;
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)