I have seen many tutorials on creating timers in React by doing something like this
useEffect(() => {
let interval = null;
if (timeractive) {
interval = setInterval(() => {
setCount(count => count+ 1);
}, 50);
} else {
clearInterval(interval);
}
return () => clearInterval(interval);
},[timeractive,count]);
This is what I think is happening:
1 timeractive = true
2 useEffect gets called, declaring assigning the variable interval to a setInterval
3 setCount gets called, incrementing count.
4 useEffect gets called again since count is one of its dependencies. But the cleanup function from the previous useEffect runs first, clearing the variable interval
5 go back to step 2
I don't want to do things this way because it seems quite unintuitive. It is essentially creating a large "interval" by creating and clearing a bunch of smaller intervals that only lasts one iteration.
So I wrote the following code, trying to create a timer that does not depend on what's being incremented. That why I decided to use useRef so that the interval will be persist between renders.
This is a timer that increases the radius every 100ms and draw a circle with that radius (modulo 50), it is supposed to be an animation but it doesn't work. However the radius seems to be updating fine. So there is something going on with the drawcircle function
I asked a similar question here, that's why I decided to pass down an updater function in setRadius
Can someone explain the concept of closure in this case?
const {useState,useEffect,useRef} = React;
function Timer({active}) {
const intervalRef = useRef(null);
const canvasRef = useRef(null);
const [width,height] = [500,500];
const [radius, setRadius] = useState(30);
useEffect(()=>{
if(active){
intervalRef.current = setInterval(()=>{
drawcircle();
setRadius(radius => radius + 1);
},100)
} else {
clearInterval(intervalRef.current)
}
},[active])
const drawcircle = ()=>{
const context = canvasRef.current.getContext('2d');
context.clearRect(0,0,width,height)
context.beginPath()
context.arc(width/2,height/2,radius%50,0,2*Math.PI)
context.stroke();
}
return (
<div>
<canvas ref={canvasRef} width={width} height={height}/>
<p>radius is {radius}</p>
</div>
)
}
function Main() {
const [active, setActive] = useState(false)
return (
<div>
<Timer active={active}/>
<button onClick={()=>{setActive(!active)}}>Toggle</button>
</div>
)
}
ReactDOM.render(<Main />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="app"></div>