Infinity loop using useState with useEffect in React

Viewed 378

I want to increase the state [second] = [second + 1] after every second.

const [second,setSecond] = useState(0);
useEffect(() => {
    const interval = setInterval(() => {
      setSecond(second+1)
    }, 1000);
    return () => clearInterval(interval);
}, []);

But it seems that an infinity loop occurs, the [second] just increases once, from 0 to 1, and it stops running.

I changed my code from

setSecond(second+1)

to

setSecond((second) => {return second+1})

And this one runs without problem:

const [second,setSecond] = useState(0);
useEffect(() => {
    const interval = setInterval(() => {
      setSecond((second) => {return second+1})
    }, 1000);
    return () => clearInterval(interval);
}, []);

Seriously, I still don't get it clearly. Can anybody explain to me why? Thanks in advance!

2 Answers

Looking at your code, I think the issue is with the value of second being bound to 0.

const [second,setSecond] = useState(0);
useEffect(() => {
    const interval = setInterval(() => {
      setSecond(second+1)
    }, 1000);
    return () => clearInterval(interval);
}, []);

Here when your component mounts, the useEffect is executed. At that time you are creating a function and passing it to setInterval. This function will use the value of second at the time of creation (which equals 0). So everytime when the setInterval runs, it is executing setSecond(0+1) which always equals 1.

The correct way that you have mentioned works because you're giving it a function which gets passed into it the current value of state everytime it is executed.

Ciao, the 2 ways you are using to set second are different.

This way:

setSecond(second+1)

does not consider the previuos value of second. It just try to increment second by 1 by reading current value of second. Considering that setSecond is async, on next setInterval is not guaranteed that second will be updated by the previous setSecond. So in this way you could have glitch.

This way:

setSecond((second) => {return second+1})

is the correct one. Here you are considering the previuos value of second (by using arrow function). So in this case, second will be correctly update.

You could make a test: Take a button and on onclick function try to write:

setSecond(second+1)
setSecond(second+1)

you will see that second will be incremented by 1 (and not by 2 as expected).

Now modify your code like this:

setSecond((second) => {return second+1})
setSecond((second) => {return second+1})

you will see that second will be incremented by 2!

This happends because Hooks are async.

Related