我一直在尝试useEffect挂钩,并且遇到了一个我不知道要解决的问题。
我编写了一个简单的计数器示例,该示例在useEffect中使用setTimeout来更新值。
if (countState.stopWatch === 0) {
dispatch({ type: 'reset' });
}
else {
const timeId = setTimeout(() => {
console.log("Timeout " + JSON.stringify(countState));
if (countState.stopWatch > 0) {
dispatch({ type: 'decrement_stopwatch' });
}
}, 1000);
return () => {
clearTimeout(timeId);
};
}
});
该代码可以正常运行,但是有一个问题。重新渲染该功能时,它将导致计时器退出并重新设置,这会引起间隔(即,计时器不是每1秒运行一次,而是每隔一秒钟运行一次,以重新渲染该应用程序)。
当我使用基于类的解决方案时,我在类构造函数中运行计时器,并且计时器运行流畅,因为它不会通过重新渲染而取消。
关于在功能版本中如何解决此问题的任何想法?
包括功能和基于类的解决方案在内的完整代码可以在这里找到:
https://github.com/jmc420/react_examples/blob/master/counter/hooks_counter/src/StopWatch.tsx
答案 0 :(得分:0)
我正尽力想出一种使用setTimeout
的方法,就像您在功能组件中尝试做的那样,但是就像您意识到的那样,渲染之间的时间间隔很小并重置超时。
我最好的选择就是使用setInterval
。如果您不确定如何将其与钩子一起使用,我将在实验中制作一个快速的Codesandbox:https://codesandbox.io/s/rough-shape-rjmjs
这里的关键是我们只设置一次间隔,只有在卸载时才将其删除,这样它才能始终如一地滴答作响。
对于第一个,我们在set状态调用内执行所有逻辑,因为它本质上可以访问最新状态args,以避免在初始状态下关闭时间间隔的问题,例如:
setCount(c => {
// c is the most recent count state
if (c > 0) {
return c - 1;
}
return c;
});
如果那不能满足您的需求,我还添加了第二个,它也使用了引用来绕过关闭问题。我们只是在每个时间间隔刻度时调用分配给ref的函数,但我们会重新分配每个渲染的函数。
timerLogicRef.current = () => {
if (otherCount > 0) {
setOtherCount(otherCount - 1);
}
};
使第二个适合您的异径管设置可能更容易。
答案 1 :(得分:0)
我找到了解决方案。如果使用useEffect时没有依赖项,则您会从react得到警告,但当计时器减少时计时器不会退出。然后,您会遇到引用过期闭包变量的问题,但这可以通过使用useRef来解决。