I am trying to create a star widget. I have a state array for each star, but when I click one of the stars, ALL of the stars set themselves to that state. I am very lost on this, please halp. I have added a lot of debugging logs. The moment I set newStars[i] = currentStar;
, the entire newStars array gets updated, but I'm failing to see why.
Also, here is the code pen link: https://codepen.io/trismi/pen/zYZpvQq?editors=1111
HTML:
<div id="root">
</div>
CSS (plus the awesome fonts stylesheet linked in the codepen)
.star {
display: inline-block;
width: 30px;
text-align: center;
color: #ddd;
font-size: 20px;
transform: scale(.8);
transition: transform 50ms ease;
&:hover,
&.semi-active {
color: gold;
transform: scale(1);
}
&.selected {
color: orange;
transform: scale(1);
}
}
JAVASCRIPT
function Star(props) {
console.log(props);
console.log(props.index);
let classes = 'star' + (props.selected ? ' selected' : '') + (props.hover ? ' semi-active' : '');
return (
<div className={classes} onClick={props.onClick}>
<i className="fas fa-star"></i>
</div>
);
}
class RatingWidget extends React.Component {
constructor(props){
super(props);
this.state = {
stars: Array(5).fill({
selected: false,
hover: false,
}),
}
}
handleClick(currentStar, index) {
console.log('\n\n\n******CLICK');
console.log("star state on click", currentStar);
console.log("index", index);
let newStars = this.state.stars.slice();
let newStar = newStars[index];
console.log("new star ", newStar);
newStar.selected = !newStar.selected;
newStars[index] = newStar;
console.log("stars", newStars);
this.setState({
stars: newStars
});
}
render() {
let stars = this.state.stars.map((rating, index) => {
return (
<Star
key={index}
index={index}
onClick={() => this.handleClick(rating, index)}
selected={rating.selected}
hover={rating.hover}
/>);
});
return (
<div className="RatingWidget">
Future rating widget
{stars}
</div>
);
}
}
ReactDOM.render(<RatingWidget />, document.getElementById('root'));
The problem is here:
Array(5).fill({
selected: false,
hover: false,
})
you are filling the same object (same reference) to each element of the array.
Try using:
Array(5).fill(null).map(() => ({
selected: false,
hover: false,
}))
Or use Array.from():
Array.from({length: 5}, () => ({ selected: false, hover: false}))