3

react state updated values are shown in use Effect but inside function only shown an old value.

const [counter, setCounter] = useState(0);

I am trying to update the counter value inside a set interval function

 const handleIncrease = () => {
clearInterval(intervalval);
if (counter < 10) {
  let increment = setInterval(() => {
    console.log("isCounterContinue@handleIncrease", counter);
    setCounter((prev) => prev + 1);
  }, 1000);
  setIntervalval(increment);
}

}; inside useEffect updated values are shown. but inside function handleIncrease only show the old value Basically, I am trying to do counter value is not increase when it's more than 30.

code Link : https://codesandbox.io/s/bold-cdn-zzbs2?file=/src/App.js

Shatish Desai
  • 575
  • 6
  • 20
  • Does this answer your question? [State not updating when using React state hook within setInterval](https://stackoverflow.com/questions/53024496/state-not-updating-when-using-react-state-hook-within-setinterval) – Tushar Shahi Jul 31 '21 at 05:53
  • 1
    The code in your codesandbox *seems* to function as you describe wanting... but it also doesn't match the snippet you've shared here. What is the issue? – Drew Reese Jul 31 '21 at 05:57
  • inside handleIncrease function counter value is not updated. so if I try to add condition(based on counter state) is not applied inside handleIncrease function – Shatish Desai Jul 31 '21 at 06:00
  • `handleIncrease` is only called when a button is clicked, with the current state. What is there to update in the click handler? I think what you are really after is accessing the updated `counter` state in the interval's callback, which "ticks" once every second. – Drew Reese Jul 31 '21 at 06:04
  • in click handler every 1000 ms after update counter state. but it is not shown updated value inside handleIncrease function. i am confused inside useEffect updated value are shown but inside handleIncrease counter value are not shown – Shatish Desai Jul 31 '21 at 06:11

1 Answers1

1

handleIncrease is only called when a button is clicked, with the current state. There is nothing to update in the click handler. I think what you are really after is accessing the updated counter state in the interval's callback, which "ticks" once every second. Or more accurately, respond to the isCounterContinue state toggling false to stop the interval when a limit is hit.

Use a ref to hold a reference to the interval timer and set/clear using this instead of a state that goes stale in enclosures.

const Timer = () => {
  const [counter, setCounter] = useState(0);
  const intervalRef = useRef();

  useEffect(() => {
    console.log({ counter });
    if (counter >= 5) {
      clearInterval(intervalRef.current);
    }
  }, [counter]);

  const handleIncrease = () => {
    clearInterval(intervalRef.current);
    intervalRef.current = setInterval(() => {
      setCounter((prev) => prev + 1);
    }, 1000);
  };

  const handleDecrease = () => {
    clearInterval(intervalRef.current);
    intervalRef.current = setInterval(() => {
      setCounter((prev) => prev - 1);
    }, 1000);
  };

  const handleStop = () => {
    clearInterval(intervalRef.current);
  };

  return (
    <>
      <div>{counter}</div>
      <div>
        <button onClick={handleDecrease}>Decrease</button>
        <button onClick={handleStop}>Stop</button>
        <button onClick={handleIncrease}>Increase</button>
      </div>
    </>
  );
};

Suggestion

The increment/decrement handlers are basically identical other than what they add to the count. Use a curried function to handle both cases by closing over an incrementing value. Since the "stop" handler shares logic to clear the interval, use the fact that 0 is a falsey value and only restart an interval timer for truthy (i.e. non-zero) number values and use one single handler for all three buttons.

const handleIncrease = (val) => () => {
  clearInterval(intervalRef.current);
  if (val) {
    intervalRef.current = setInterval(() => {
      setCounter((prev) => prev + val);
    }, 1000);
  }
};

...

<button onClick={handleIncrease(-1)}>Decrease</button>
<button onClick={handleIncrease(0)}>Stop</button>
<button onClick={handleIncrease(1)}>Increase</button>

Edit updated-state-value-are-not-updated-inside-function-in-react

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • it's working, can you please explain the reason behind why without Ref, updated value is not shown inside handleIncrease function. – Shatish Desai Jul 31 '21 at 06:14
  • 1
    @ShatishDesai It is only displaying the current `counter` value when clicked, not when the interval "ticks". If you continued to click the "Increase" button each second you'll see the updated `counter` state. If you wait a few seconds and *then* click "Increase" again, you'll see the current `counter` state. – Drew Reese Jul 31 '21 at 06:18