I want to call a function at regular intervals and update state inside it.
I started with a useEffect but eventually ended up here. I tried using Ref.current to store the interval and clear it every time in the function and recreate it but it still wasn’t working. Something like this
const drawBall = () => {
clearInterval(intervalRef.current);
// Access state and update it
intervalRef.current = setInterval(drawBall, 1000);
}
I dropped the idea and thought of using a simple setTimeout. Everytime drawBall
is called, it calls a new Timeout, even if the closure takes the value at the time of being called, it still will have the updated value as the value at the time of calling is updated. But it is not working as expected, the function still gets the old value even though the value is being updated correctly. The code goes like this –
const drawBall = () => {
console.log(ballPos); // Shows initial value in every call (not the expected behaviour)
setBallPos(prevVal => {
console.log(prevVal); // Shows the right value
return {
x: prevVal.x + 2,
y: prevVal.y + 2
}
});
setTimeout(drawBall, 1000);
}
What am I missing here?
Edit:
Switching to use-effect doesn’t help. It updates ballPos
correctly, but that was happening before too. The issue still remains that when I access ballPos.x
even inside the same useEffect, it shows the original value, not the updated one.
This is the new useEffect code
useEffect(() => {
const interval = setInterval(() => {
const canvas = gameBoard.current;
const ctx = canvas.getContext('2d');
let currX = ballPos.x;
let currY = ballPos.y;
currX += defaultDX;
currY += defaultDY;
console.log(currX, currY); // Shows Old Value
ctx.fillStyle = "#FF0000";
ctx.beginPath();
ctx.arc(currX, currY, 10, 0, Math.PI * 2, true);
ctx.fill();
setBallPos((prevVal) => {
return {
x: prevVal.x + defaultDX,
y: prevVal.y + defaultDY
}
});
}, 1000);
return () => {
clearInterval(interval);
}
}, [])
2
Answers
To resolve the issue and to continuously update the state at regular intervals, you should consider using the
useEffect
hook along withsetInterval
to update the state.While you’re updating the state correctly, you continue to use the old value to draw on the canvas because the
ballPos
reference inside the interval callback is not aware of state changes.You could use the new state value for the draw call as shown below, or move the
requestAnimationFrame
part to its own Effect entirely.