I have a function react component that has a counter that starts from 10000 and goes to 0.
I am setting a setInterval callback using useEffect hook during component mounting. The callback then updates the counter state.
But I don't know why, the count value never decreases. Each time the callback runs count is 10000.
(I am using react & react-dom version 16.8.3)
Function component is as below:
import React, { useState, useEffect, useRef } from 'react'
const Counter = () => {
  const timerID = useRef()
  let [count, setCount] = useState(10000)
  useEffect(() => {
    timerID.current = setInterval(() => {
      //count here is always 10000
      if (count - 1 < 0) {
        setCount(0)
      } else {
        setCount(count - 1)
      }
    }, 1)
  }, [])
  return <h1 className="counter">{count}</h1>
}
export default Counter
Here is the link to codesandbox: link
You need to watch for changes in count, and also clean up your useEffect():
useEffect(() => {
    timerID.current = setInterval(() => {
      if (count - 1 < 0) {
        setCount(0)
      } else {
        setCount(count - 1)
      }
    }, 100)
    return () => clearInterval(timerID.current);
  }, [count])
As @Pavel mentioned, Dan Abramov explains why here.
There are 2 options:
1) Include count in the dependencies
This is not ideal, as it means a new setInterval will be created on every change of count, so you would need to clean it up on every render, example: 
  useEffect(() => {
    timerID.current = setInterval(() => {
      //count here is always 10000
      if (count - 1 < 0) {
        setCount(0)
      } else {
        setCount(count - 1)
      }
    }, 1)
    return () => clearInterval(timerID.current) // Added this line
  }, [count]) // Added count here
2) Add the count in the setInterval callback function. 
This is the best approach for intervals, as it avoids, setting new ones all the time.
 useEffect(() => {
    timerID.current = setInterval(() => {
      // count is used inside the setCount callback and has latest value
      setCount(count => {
        if (count - 1 < 0) { // Logic moved inside the function, so no dependencies
          if (timerID.current) clearInterval(timerID.current)
          return 0
        }
        return count - 1
      })
    }, 1)
    return () => {
      if (timerID.current) clearInterval(timerID.current) // Makes sure that the interval is cleared on change or unmount
    }
  }, [])
Here is the sandbox link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With