0

Can Anyone tell me the difference between this :

function IntervalHookCounter() {
const [count,setCount] = useState(0)

useEffect(()=>{
    const interval = setInterval(tick,1000)
},[])

const tick = ()=>{
    setCount(count+1)
}

And this :

function IntervalHookCounter() {
const [count,setCount] = useState(0)

useEffect(()=>{
    const interval = setInterval(tick,1000)
},[])

const tick = ()=>{
    setCount(prevState=>prevState+1)
}

I don't understand why the first one is not woriking.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Nomear77
  • 31
  • 5

3 Answers3

1

Because the useEffect call that's run exactly once captures the "instance" of the tick function it sees at the time the component mounts. That "instance" of the tick function will have captured the value of count, which is initially 0.

Thus, that setInterval basically always runs setCount(0 + 1).

The second implementation doesn't do that, as the functional update form of setState has React itself provide the previous value of the state atom to the update function, so there's no "stale" count to have been captured.

AKX
  • 152,115
  • 15
  • 115
  • 172
  • so why when I pass **count** to the dependency array it works fine ? – Nomear77 Sep 05 '22 at 18:04
  • @Nomear77 The `useEffect`'s dependency array? Because then a new interval, with a new captured `tick`, will be scheduled. Note that you'll then have overlapping interval functions, since your effect is currently not cleaning anything up. – AKX Sep 05 '22 at 18:08
1

Ok, so this question isn't actually about useEffect but about SetStateAction.

Ie:

setState(tick + 1);
vs
setState(tick => tick + 1);

The answer has to do with concurrency. What if you call setState(tick + 1), twice in the same render? You will update it to the same value, instead of incrementing twice like you would expect. If you pass a function to setState you will get as the first argument the latest value, instead of the value that was bound to your render.

Edit:

One issue that your code has:

function IntervalHookCounter() {
const [count,setCount] = useState(0)

useEffect(()=>{
    const interval = setInterval(tick,1000)
},[])

const tick = ()=>{
    //...
}

Is that you're not managing your dependencies accurately.

Your useEffect has a depdency on tick, and tick has dependency on count. Except in the 2nd example it doesn't have that dependency on count.

If you install a linting tool you will see warnings that you're missing dependencies in your code. And in the first example it's actually a problem. If the first example, adding tick as a dependency will cause the code to behave in unintended ways, especially since you're not cleaning up your setInterval in the useEffect.

Halcyon
  • 57,230
  • 10
  • 89
  • 128
0

This happens when the new state value depends on the previous state. Since the state update operation is a batch operation, the operation without knowing the previous state may lead you to the wrong result. Therefore, if multiple state update calls are made at the same time, the old state value is obtained first and updated over it.

Ogün Birinci
  • 598
  • 3
  • 11
  • This particular issue is not caused by batched state updates. Please see my answer. – AKX Sep 05 '22 at 11:54