ReactJS progress bar dynamically updating using state?

4.2k views Asked by At

I am using the PrimeReact progress bar and would like to get it dynamically update when the page is loaded up. My code is below -

const IntroPage = () => {
  const [progressBarValue, setProgressBarValue] = useState(0)
  useEffect(() => {
    setInterval(function() {
      let val = progressBarValue;
      val += Math.floor(Math.random() * 10) + 5;

      if (val >= 100) {
        val = 100;
      }

      setProgressBarValue(val)
    }, 1000);
  }, [])

  return (
    <div className='main-container'>
      <div></div>
      <div className='progress-bar-container'>
        <ProgressBar className='progress-bar' value={progressBarValue}></ProgressBar>
      </div>
      <div></div>
    </div>
    )
}

For some reason when I load the page up, the progress bar doesn't continually add up to 100, it goes back and forth and I'm not too sure why this is happening. So it will go to 8 or 10%, then drop down to 6, etc. I think the issue is that each time the new state is being set, it's not adding on top of the previous set state. Any help would be appreciated.

Update: I've also tried putting the setInterval in a separate function and calling it but this also does the same thing -

 useEffect(() => {
             loadProgressBar() 
        }  
    }, [])

 function loadProgressBar() {
        setInterval(function(){ 
            let val = progressBarValue;
            val += Math.floor(Math.random() * 10) + 5;

            if (val >= 100) {
                val = 100;
                clearInterval();
            }

            setProgressBarValue(val)
        }, 1000);
    }

1

There are 1 answers

2
Tasos On BEST ANSWER

This is not the correct way to use an interval function inside the useEffect you can read more here

The right way is this:

useEffect(() => {
    const intervalId = setInterval(() => {
      setProgressBarValue((prev) => {
        if (prev >= 100) {
          clearInterval(intervalId);
          return 100;
        } else {
          return prev + 5;
        }
      });
    }, 1000);
    return () => clearInterval(intervalId);
  }, []);

LIVE DEMO

The problem with your implementation is that the progressBarValue is not updated inside the useEffect.
The callback of the setInterval is compiled once and then runs (the same value/reference of the function and its arguments) every 1000ms because the dependency array is empty.
This means, that it reads the initial value of the progressBarValue and doesn't read the next values simple because it is not a dependency for the useEffect.

In my implementation the setProgressBarValue() is using the callback to read the previous value of the state and increase it.