jamesvphan jamesvphan - 3 years ago 256
Javascript Question

React - Update style with both onClick and onMouseOver events

I'm creating a rating system to rate images. I'd like to apply both onClick and onMouseOver events to the stars that I'm rendering for the system.

onMouseOver: depending on which star the mouse is hovering, should fill in the star as well as the stars before the hovered star. If I leave the div container the stars, then no stars will be filled in.
Ex. with 4 stars, if I hover over the 3rd star, then stars 1-3 should be filled in. If I don't hover over any stars, no stars should be filled in.

onClick: similar to above except stars will be filled in regardless if hovering over or away from the stars.
ex. click on star 3 and stars 1-3 will be filled in. Should stay filled in and onMouseOver/onMouseLeave effects are disabled.

Link to code: https://codesandbox.io/s/k0zmr3m8po

Function to render stars
class Stars extends Component {
constructor() {
super()

this.state = {
ratingScore: 0,
starsOnHover: 0
}

this.handleOnHoverStars = this.handleOnHoverStars.bind(this)
this.clearRatingScore = this.clearRatingScore.bind(this)
this.updateRatingScore = this.updateRatingScore.bind(this)
}

handleOnHoverStars(e) {
e.preventDefault()
this.setState({
starsOnHover: e.currentTarget.id
})
}

clearRatingScore(e) {
e.preventDefault()
this.setState({
starsOnHover: 0
})
}

updateRatingScore(e) {
e.preventDefault()
this.setState({
ratingScore: e.currentTarget.id
})
}

renderRatingStars() {
let stars = []
for (let i = 0; i < 4; i++) {
stars.push(
<a href="#"
key={i}
id={i+1}
onMouseOver={this.handleOnHoverStars}
onMouseLeave={this.clearRatingScore}
onClick={this.updateRatingScore}
>
<svg width="38" height="36" viewBox="0 0 38 36" preserveAspectRatio="xMidYMid meet">
<path d="M19.022 29.348L7.577 35.394l2.186-12.806-9.26-9.069L13.3 11.651 19.022 0l5.723 11.65 12.796 1.87-9.26 9.068 2.186 12.806z" fill={i < this.state.starsOnHover ? "#000": "#fff"} stroke="#c2c2c2" strokeWidth="1" fillRule="evenodd"></path>
</svg>
</a>
)
}
return stars
}

render() {
return (
<div>{this.renderRatingStars()}</div>
)
}
}


I've so far got the onMouseEnter and onMouseLeave to work by updating state and checking which star I'm currently hovering over, thereby updating the fill prop style of my svg element. Where I'm stuck is then calling the onClick event and ensuring that hover events won't override the style as onClick should trump onMouseOver/onMouseLeave. I was thinking of assigning to the fill prop of svg element a function that will return a ternary expression depending on whether the stars were clicked or not, but not sure if you can return ternary expressions.

Answer Source

The only change that I made to your code to make it run was to change the fill attribute in the svg component to the following

fill={i < this.state.starsOnHover || i<this.state.ratingScore ? "#000": "#fff"}

This would check each star's id with the state and fill it if

  1. it is less than the hovered star's id
  2. it is less than the clicked star's id

Hope that is what you wanted to achieve.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download