0

In React I like to increment a state-value (progressValue) every 500 ms from 0 to 100. This is what I do:

    const [progressValue, setProgressValue] = React.useState<number>(0)

    const tick = () => {
        console.log("progressValue: " + progressValue)
        if (progressValue >= 100) {
            setProgressValue(100)
        } else {
            const newValue = progressValue + 0.1
            setProgressValue(newValue)
            setTimeout(tick, 500)
        }
    }

    const onPlayClicked = () => {
        setProgressValue(0)
        tick()
    }

Unfortunately the log says

progressValue: 0
progressValue: 0
progressValue: 0
...

What's wrong?

EDIT

This worked finally:


    const [progressValue, setProgressValue] = React.useState<number>(0)

    React.useEffect(() => {
        const interval = setInterval(() => {
            if (progressValue < 100) {
                setProgressValue((prevVal) => prevVal + 1);
            }
        }, 500);
        return () => clearInterval(interval);
    }, [progressValue]);
4
  • 1
    You can do that in useEffect. Commented Jun 13, 2023 at 13:03
  • Call your state updater in a setinterval to create in useEffect Commented Jun 13, 2023 at 13:04
  • using useEffect could be the ideal way. Commented Jun 13, 2023 at 13:06
  • I was not able to find a working solution with useEffectand setInterval. The examples here stackoverflow.com/questions/72592921/… don't work, too... Commented Jun 13, 2023 at 13:27

1 Answer 1

1

The reason why its not incrementing is to do with references. when you execute the onPlayClicked function. It sets the progressValue to 0 and when tick is executed the progressValue which is still 0 is set to 0.1. we are then again calling the tick function which contains the same progressValue reference set to 0. Hence, the progress value always stays the same.

Now coming to solving this issue. The easy way to solve this is by just using the previous state. Inside your tick function in the setProgressValue you can just do the following.

setProgressValue(prevState=>prevState+0.1);

Full working snippet:

const {useState} = React;
const App = () => {
  const [progressValue, setProgressValue] = React.useState(0);

  const tick = () => {
    if (progressValue >= 100) {
      setProgressValue(100);
    } else {
      setProgressValue((prev) => prev + 0.1);
      setTimeout(tick, 500);
    }
  };

  const onPlayClicked = () => {
    setProgressValue(0);
    tick();
  };

  return (
    <div className="App">
      <h1>Demo</h1>
      <span>{progressValue}</span>
      <br />
      <button onClick={onPlayClicked}>Click Me</button>
    </div>
  );
}

ReactDOM.createRoot(
    document.getElementById("root")
).render(
    <App />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

Sign up to request clarification or add additional context in comments.

2 Comments

You can use toFixed to avoid 0.00000001
i know it was just a demo for updating values. so made changes to the bare mimimum

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.