Reputation: 59
Can you please explain me why this simple code doesn't work as expected ? There's a button called "Start the timer" that's directing to the "chrono" function. I thought that using setCount would update the count variable state but it stays at 0 every second. Isn't useState used precisely to update variables ? Thanks for your help ! PS : in the button, I just rename onClick to clicAction to use it in my component but this is not a problem at all.
import { Button } from './components/Button';
import { useState, useEffect } from 'react';
function App() {
const [count, setcount] = useState(0);
const chrono = () => {
setInterval(() => {
setcount(count +1);
console.log(count);
},
1000);
}
return (
<>
<Button clicAction={chrono} nom="Start the timer"/>
<p>{count}</p>
</>
);
}
export default App;
And here's the Button component code :
import { useState } from 'react';
export const Button = ({nom, clicAction}) => {
return (
<div>
<button onClick={clicAction}>{nom}</button>
</div>
)
}
Upvotes: 3
Views: 77
Reputation: 533
The reason it does not work is because you are using the current count
value to determine the next count
value. It might not work as expected because as mentioned in the React official documentation, React tends to call setState
asynchronously for performance reason. Therefore for state, you should not rely on their values for calculating the next state. ~React. Instead, you might want to pass a function to the setCount argument like this:
const chrono = () => {
setInterval(() => {
setcount(prevCount => prevCount + 1);
},
1000);
}
As mentioned by @Martin, this behavior is caused not by asynchronicity but closure. So my explanation that mentioned the asynchronous behavior was not correctly used in this context. For the problem above, using function as setState
argument is still correct in case of closures. Please refer to @DrewReese's answer if you are looking for closure explanation.
Upvotes: 0
Reputation: 203522
This is a stale enclosure of the count
state in the interval callback. The value of count
is never a newer reference. Use a functional state update to correctly update from the previous count
state value.
If you want to log state updates then use an useEffect
to log the count
state when it updates.
const [count, setCount] = useState(0);
useEffect(() => {
console.log(count);
}, [count]);
const chrono = () => {
setInterval(() => {
setCount(count => count +1);
}, 1000);
}
Upvotes: 2