我已经看到了很多关于在 React 中创建计时器的教程,通过做这样的事情
useEffect(() => {
let interval = null;
if (timeractive) {
interval = setInterval(() => {
setCount(count => count+ 1);
}, 50);
} else {
clearInterval(interval);
}
return () => clearInterval(interval);
},[timeractive,count]);
这就是我认为正在发生的事情:
1timeractive = true
调用 2useEffect
,声明将变量interval
分配给setInterval
3setCount
被调用,递增count
.
4useEffect
再次被调用,因为count
是其依赖项之一。但是前一个useEffect
的清理函数首先运行,清除变量interval
5 返回步骤 2
我不想以这种方式做事,因为它看起来很不直观。它本质上是通过创建和清除一堆仅持续一次迭代的较小间隔来创建大的"间隔"。
所以我写了下面的代码,试图创建一个不依赖于增量的计时器。这就是为什么我决定使用 useRef,以便在渲染之间保持间隔。
这是一个计时器,每 100 毫秒增加一次半径并用该半径(模 50(画一个圆,它应该是一个动画,但它不起作用。但是半径似乎更新得很好。所以drawcircle
函数发生了一些事情
我在这里问了一个类似的问题,这就是为什么我决定在setRadius
中传递更新程序函数
的原因在这种情况下,有人可以解释关闭的概念吗?
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>
间隔回调使用每次迭代时第一个渲染的drawcircle
方法,并且该方法drawcircle
引用初始半径值。要解决此问题,请使用 ref todrawcircle
方法
drawcircleRef = useRef()
useEffect(()=>{
if(active){
drawcircleRef.current = drawcircle
const interval= setInterval(()=>{
drawcircleRef.current();
setRadius(radius => radius + 1);
},100)
return ()=> clearInterval(interval)
}
},[active])
useEffect(()=>{
drawcircleRef.current = drawcircle
},[radius])