1

I have this component that changes background color based on the school's rating.

Between 1 to 10, if the school's rating 3 and below should be orange, between 4 and 7 and should be yellow, 8 and above should be green. If the school does not have a rating (null), should be gray.

Here's my attempt:

...

  const [bg, setBg] = useState('gray')

  const Single = ({rating, name, distance}: Single) => {
    if (rating && rating <= 3) {
      setBg(`orange`)
    } else if (rating && rating >= 4 && rating <= 7) {
      setBg(`yellow`)
    } else if (rating && rating >= 8) {
      setBg(`green`)
    }

    return (
      <div>
        <span backgroundColor={bg}>
          {rating !== null ? rating : `NA`}
        </span>
      </div>
    )
  }

...

But now everything is green, even though I tested with various numbers.

What am I doing wrong?

halfer
  • 19,824
  • 17
  • 99
  • 186
Swix
  • 1,883
  • 7
  • 33
  • 50

2 Answers2

2

See the Rules of Hooks and the code samples within. useState must be called within the body of your component, like this:

const Single = ({rating, name, distance}: Single) => {
    const [bg, setBg] = useState('gray')

    if (rating && rating <= 3) {
      setBg(`orange`)
    } else if (rating && rating >= 4 && rating <= 7) {
      setBg(`yellow`)
    } else if (rating && rating >= 8) {
      setBg(`green`)
    }

    return (
      <div>
        <span backgroundColor={bg}>
          {rating !== null ? rating : `NA`}
        </span>
      </div>
    )
  }
naiveai
  • 590
  • 2
  • 14
  • 42
  • 2
    @Rario because you are setting using `setBg` unconditionally (if you copied my code exactly), everytime a render is happening, regardless of if `rating` has actually changed, `setBg` is run again, causing yet another render and so on and so forth. Derived state based on props is generally done using `useEffect` or `useMemo` - see another SO question on this: https://stackoverflow.com/questions/54625831/how-to-sync-props-to-state-using-react-hooks-setstate – naiveai Jul 02 '20 at 05:32
  • 1
    Here in this code, each time the state gets updated component will get re-render. And on each render you are again setting the state and the loop goes on hence you are getting `Too many re-renders. .....`. – Nithish Jul 02 '20 at 05:34
1

you need useMemo here

const bg = useMemo(() => {
    if (rating && rating <= 3) {
        return 'orange'
      } else if (rating && rating >= 4 && rating <= 7) {
        return 'yellow'
      } else if (rating && rating >= 8) {
        return 'green'
      }
}, [rating])

so now useMemo function callback will be called only if the value of rating gets changed and save the returnd value to const bg

if you are expecting the background-color of the span to be set based on the value of bg, you have to change the span definition as below

<span style={{backgroundColor: bg}}>...
Kalhan.Toress
  • 21,683
  • 8
  • 68
  • 92
  • Alternatively just do it without hooks, I don't think such a simple condition would make it worthwhile to cache the result of it. You can calculate the correct background color on each re-render. – Grabofus Jul 13 '20 at 13:20