如何在我的 React 组件中使用清除计时器?



当我尝试从控件重置计数器时,我无法弄清楚为什么我的计数器无法重置。我怀疑我在如何从我的控制范围内操纵状态方面犯了某种新手(常见且不尴尬(错误。

例如,如果我多次计时"更快",然后单击"正常"计数继续加速:显然更快的计时器尚未通过调用startTimer清除。只有随后单击"重置"或"停止",然后单击"开始",更快的计时器才会显示为清除。但我对为什么会这样感到困惑:所有路径都以相同的方式使用clearInterval

我怀疑我没有掌握一些关于如何在组件中操纵状态的一般知识;或者也许是如何从组件状态正确访问计时器。

为什么我无法按预期清除计时器?

WobblyCounter.tsx

import React, { useState } from 'react'
import { View, Button, Text } from 'native-base'
import { useDispatch, useSelector } from 'react-redux'
const WobblyCounter = () => {
const [ timerID, setTimerID ] = useState(0)
const [ isRunning, updateIsRunning ] = useState(false)
const [ interval, updateInterval ] = useState(1000)
const count = useSelector((state) => state.count)
const dispatch = useDispatch()
const startTimer = (): void => {
clearInterval(timerID)
setTimerID(setInterval(() => { dispatch( {type: "INCREMENT", step: 1} ) }, interval))
updateIsRunning(true)
}
const stopTimer = (): void => {
clearInterval(timerID)
updateIsRunning(false)
}
return (
<View style={ {paddingTop:50} }>
<Button
onPress={ (): void => { dispatch( {type: "RESET"} ); startTimer() } }>
<Text>Reset</Text>
</Button>
<View style={ {flexDirection: "row"} }>
<Button small bordered dark disabled={ interval <= 250 }
onPress={ (): void => { updateInterval(Math.max(interval - 250, 250)); startTimer() } }>
<Text>Faster</Text>
</Button>
<Button small bordered dark disabled={ interval == 1000 }
onPress={ (): void => { updateInterval(1000); startTimer() } }>
<Text>Normal</Text>
</Button>
<Button small bordered dark
onPress={ (): void => { updateInterval(interval + 250); startTimer() } }>
<Text>Slower</Text>
</Button>
</View>
<Button small style={ Object.assign( {}, {backgroundColor: isRunning ? "red" : "green"} ) }
onPress={ (): void => { isRunning ? stopTimer() : startTimer() } }>
<Text>{isRunning ? "Stop" : "Start"}</Text>
</Button>
<Text>
Debug{"n"}count = {count}{"n"}interval = {interval}{"n"}timerID = {timerID}
</Text>
</View>
)
}
export default WobblyCounter

这里的主要问题是闭包startTimer使用的是旧状态值:

  1. 在第一个渲染 timerID=0 和 interval=1000 时,使用这些值创建startTimer
  2. 当您单击"更快"时,将调用updateInterval并且间隔状态更改为 750,但组件尚未呈现,因此使用旧值 interval=1000 调用startTimer
  3. 组件重新呈现 timerID=1 和 interval=750,startTimer使用这些值重新创建。
  4. 当您单击"正常"时,将调用updateInterval并且间隔状态更改为 1000,但组件尚未呈现,startTimer使用旧值 interval=750 调用。这就是为什么计数器仍然运行得很快的原因。

解决此问题的一种方法是使用 Dan Abramov 在此处提出的自定义钩子useInterval,并且仅在单击按钮时更新相关状态(间隔,isRunning(。

useInterval(
() => {
dispatch({ type: "INCREMENT", step: 1 });
},
isRunning ? interval : null
);

你可以在这里找到完成的代码(我删除了反应原生(

您可以通过使用空 dep 数组清理useEffect来重置计时器。

这意味着此副作用将在组件卸载时运行一次

useEffect(() => {
// startTimer will run once on component mount
startTimer();
// The cleanup callback will run once on component unmount
return stopTimer;
}, []);

但是,在您的情况下,您永远不会卸载组件(因为您在单击按钮时调度操作,这意味着您始终处于更新周期(,请尝试在stopTimer处放置断点。

最新更新