我正在构建一个秒表 UI,以秒为单位显示时间。单击按钮,计时器将开始向上计数,并在再次单击时停止。用户应该能够再次启动它。
我遇到的问题是,我可以让 setInterval
正常工作,但是一旦包含 setTime
Hook ,组件就会更新以在 UI 中呈现时间,但setInterval
实例被多次调用。这会导致奇怪的渲染行为。
const Timer = () => {
const [time, setTime] = useState(0)
let timer
const startStopTimer = () => {
if (!timer) timer = setInterval(() => setTime(time++), 1000)
else {
clearInterval(timer)
timer = null
}
}
return (
<div>
<p>Time: {time} seconds</p>
<Button
onClick={() => {
startStopTimer()
}
> Start/Stop </Button>
</div>
)
}
示例行为是:
- 用户点击“开始/停止”
- 计时器从 0 开始向上计数
- 用户点击“开始/停止”
- 计时器立即停止
- 用户点击“开始/停止”
- 计时器从上次停止的地方继续
最佳答案
这是 React hook 中过时闭包的典型示例,在调用 setTime
后,time
的 setInterval 值不会改变。更改您的代码:
setInterval(() => setTime(currentTime => currentTime + 1), 1000)
。
setTime
就像有类组件的 setState
一样,也接受一个回调函数,该函数将当前值作为第一个参数
此外,timer
变量在您的代码中毫无用处,因为每次重新渲染时它都将是未定义的,并且您将无法访问 setInterval
的返回值,因此它将重新初始化setInterval
。要处理该使用 useRef
,您可以将 setInterval
的返回值存储在 .current
中,在后续重新渲染后您将可以使用它,因此不需要更多重新初始化 setInterval ,您还可以使用 clearInterval
解决方案:
const {useState, useRef} = React;
const {render} = ReactDOM;
const Timer = () => {
const [time, setTime] = useState(0);
const timer = useRef(null);
const startStopTimer = () => {
if (!timer.current) {
timer.current = setInterval(() => setTime(currentTime => currentTime + 1), 1000);
} else {
clearInterval(timer.current);
timer.current = null;
}
};
return (
<div>
<p>Time: {time} seconds</p>
<button
onClick={startStopTimer}
>
Start/Stop
</button>
</div>
);
};
render(<Timer />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
关于javascript - setInterval + React hooks 导致组件内多次更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63458425/