Reputation: 1150
export default function Timer() {
const [timer, setTimer] = useState(0)
const checkTimer = () => {
console.log(timer);
}
useEffect(() => {
const timer = setInterval(() => {
setTimer(prevCount => prevCount + 1);
}, 1000);
startProgram(); //This starts some other functions
return () => {
checkTimer();
clearInterval(timer);
}
}, [])
}
Above is a simplified version of my code and the main issue - I am trying to increase the timer
state by setting an interval in useEffect() (only once). However, in checkTimer()
the value is always 0, even though the console statement execute every second. I am new to reactjs and would appreciate some help as this is already taking me too many hours to fix.
Upvotes: 2
Views: 767
Reputation: 19823
checkTimer
is showing you the initial value of timer
state because of stale closure. That means at the time when useEffect
was executed, (i.e. once at component mount due to []
as dependency), it registered a cleanup function which created a closure around checkTimer
function (and everything, state or props values, it uses). And when this closure was created the value of timer
state was 0
. And it will always remain that.
There are few options to fix it.
One quick solution would be to use useRef
:
const timer = useRef(0);
const checkTimer = () => {
console.log(timer.current);
};
useEffect(() => {
const id = setInterval(() => {
timer.current++;
}, 1000);
return () => {
checkTimer();
clearInterval(id);
};
}, []);
Check this related post to see more code examples to fix this.
And, if you want to show the timer
at UI as well, we need to use state as we know that "ref" data won't update at UI. So, the option 2 is to use "updater" form of setTimer
to read the latest state data in checkTimer
function:
const [timer, setTimer] = useState(0);
const checkTimer = () => {
let prevTimer = 0;
setTimer((prev) => {
prevTimer = prev; // HERE
return prev; // Returning same value won't change state data and won't causes a re-render
});
console.log(prevTimer);
};
useEffect(() => {
const id = setInterval(() => {
setTimer((prev) => prev + 1);
}, 1000);
return () => {
checkTimer();
clearInterval(id);
};
}, []);
Upvotes: 2