0

I'm building a rating component that takes in a number of icons and displays them using react-icons. My question however is for a feature that I would like to implement. Currently, the react-icon that I'm using is FaStarhalf which is a pre-filled half star, that I flip to make the illusion that I've created half stars. This is very easy to work with and does what I need it to do, however, I wanted to implement a better way to display when you are selecting a star. Unfortunately, react-icons doesn't seem to have the ability for me to fill an icon all the way. FaRegStarHalf is another component from react-icons which is basically a cut out of a star, however, you can't fill it in.

So my question to you. Would it be possible inside my FaStarHalf component, when it is clicked, to change every FaStarHalf into it's filled counterpart from react-icons? Can I change a component from inside itself to be a different component?

Rater

import React, { useState } from 'react'
import { FaStarHalf } from 'react-icons/fa'

import './styles/Rater.css'

const Rater = ({ iconCount }) => {
    const [rating, setRating] = useState(null)
    const [hover, setHover] = useState(null)
    return (
        <div>
            {[...Array(iconCount), ...Array(iconCount)].map((icon, i) => {
                const value = (i + 1) / 2

                return (
                    <label>
                        <input
                            type='radio'
                            name='rating'
                            value={value}
                            onClick={() => {
                                console.log(`value => `, value)
                                return setRating(value)
                            }}
                        />
                        <div className='star-container'>
                            <div>
                                <FaStarHalf
                                    className={i % 2 ? 'star-left' : 'star'}
                                    color={value <= (hover || rating) ? '#ffc107' : '#e4e5e9'}
                                    onMouseEnter={() => setHover(value)}
                                    onMouseLeave={() => setHover(null)}
                                />
                            </div>
                        </div>
                    </label>
                )
            })}
        </div>
    )
}

export default Rater
FaHalfStar

enter image description here

FaRegHalfStar

enter image description here

Francis Gagnon
  • 3,545
  • 1
  • 16
  • 25
4156
  • 380
  • 4
  • 17
  • Figuring out what you're after is a bit confusing but why not just use the filled star from react-icons as needed? `FaStar`. So if a rating is 3.5 stars display 3 `FaStar` icons and one `FaHalfStar` icon. Perhaps I'm misunderstanding the problem you're having though? It might help to see what your data from `...Array(iconCount)` is. – CaseyC Dec 18 '20 at 23:23

2 Answers2

1

Without seeing what the data you're working with looks like this is difficult to answer but I'll take a shot. I'm assuming that what you want is when a user clicks a star to set a rating between 1 and 5, you want to show their rating to them in the form of filled stars. If that's the case you can use rating to determine whether the user is being shown the current rating or the rating that they've set and then use the proper icon as needed.

This can be accomplished by setting the proper string to a variable and then using that in place of your icons JSX tag name. Basically, if the user has set a rating, rating will no longer be null and you can assert that you need filled stars. Use that logic to assign a variable to the string you want. const Star = rating ? 'FaStar' : 'FaHalfStar'

import React, { useState } from 'react'
import { FaStarHalf, FaStar } from 'react-icons/fa'

import './styles/Rater.css'

const Rater = ({ iconCount }) => {
    const [rating, setRating] = useState(null)
    const [hover, setHover] = useState(null)
    // check if user has set a rating by clicking a star and use rating to determine icons
    const Star = rating ? 'FaStar' : 'FaHalfStar'

return (
    <div>
        {[...Array(iconCount), ...Array(iconCount)].map((icon, i) => {
            const value = (i + 1) / 2              
            
            return (
                <label>
                    <input
                        type='radio'
                        name='rating'
                        value={value}
                        onClick={() => {
                            console.log(`value => `, value)
                            return setRating(value)
                        }}
                    />
                    <div className='star-container'>
                        <div>
                            // Use 'Star' variable here which is assigned 'FaStar' or 'FaHalfStar' and will display a half star or a full star
                            <Star
                                className={i % 2 ? 'star-left' : 'star'}
                                color={value <= (hover || rating) ? '#ffc107' : '#e4e5e9'}
                                onMouseEnter={() => setHover(value)}
                                onMouseLeave={() => setHover(null)}
                            />
                        </div>
                    </div>
                </label>
            )
        })}
    </div>
)
}

export default Rater
FaHalfStar
CaseyC
  • 1,453
  • 14
  • 23
  • This is similar to the answer I'm kind of looking for. What I want is, when you first over over them, you are given the 2nd image, but when you click and make your choice, they become filled in like the 1st image. I ran your code and the string values didn't work, but when I changed them to just literal FaStar, it ran, but there was no change between my code and yours. However, I think you are on the right track for what I'm looking for and this helps me out alot – 4156 Dec 19 '20 at 00:19
  • I was able to move around the code and find a solution – 4156 Dec 19 '20 at 00:29
  • Glad you found your solution. I threw together a quick code pen to show that you can change the tag type depending on state https://codepen.io/choincstar/pen/vYXZjrY – CaseyC Dec 19 '20 at 00:41
0

I was able to figure it out, using a similar code as to what @CaseyC used, I needed to find the corresponding Halfstar that was filled in, not the full star I was trying to use

import React, { useState } from 'react'
import { FaRegStarHalf, FaStarHalf } from 'react-icons/fa'

import './styles/Rater.css'

const Rater = ({ iconCount }) => {
    const [rating, setRating] = useState(null)
    const [hover, setHover] = useState(null)
    // check if user has set a rating by clicking a star and use rating to determine icons
    const Star = rating ? FaStarHalf : FaRegStarHalf

    return (
        <div>
            {[...Array(iconCount), ...Array(iconCount)].map((icon, i) => {
                const value = (i + 1) / 2

                return (
                    <label>
                        <input
                            type='radio'
                            name='rating'
                            value={value}
                            onClick={() => {
                                console.log(`value => `, value)
                                return setRating(value)
                            }}
                        />
                        <div className='star-container'>
                            <div>
                                <Star
                                    className={i % 2 ? 'star-left' : 'star'}
                                    color={value <= (hover || rating) ? '#ffc107' : '#e4e5e9'}
                                    onMouseEnter={() => setHover(value)}
                                    onMouseLeave={() => setHover(null)}
                                />
                            </div>
                        </div>
                    </label>
                )
            })}
        </div>
    )
}

export default Rater
4156
  • 380
  • 4
  • 17