当我尝试从控件中重置计数器时,我很难弄清楚为什么我的计数器不会重置。我怀疑我在如何从控件内部操纵状态方面犯了一些新手(常见且不尴尬)的错误。
例如,如果我多次单击“ Faster”(快速),然后单击“ Normal”(正常),则计数将以加速的速度继续进行:显然,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
答案 0 :(得分:1)
这里的主要问题是闭包startTimer
使用的是旧状态值:
startTimer
。updateInterval
并将间隔状态更改为750,但该组件尚未渲染,因此使用旧值interval =调用startTimer
1000。startTimer
。updateInterval
并将间隔状态更改为1000,但尚未渲染该组件,将以旧值interval = 750调用startTimer
。这就是为什么计数器仍在快速运行的原因。解决此问题的一种方法是使用自定义钩子useInterval
proposed here by Dan Abramov,并且仅在单击按钮时更新相关状态(间隔,isRunning)。
useInterval(
() => {
dispatch({ type: "INCREMENT", step: 1 });
},
isRunning ? interval : null
);
您可以找到完整的代码here(我删除了react-native)
答案 1 :(得分:0)
您通过cleanup callback通过useEffect
的{{3}}重置了计时器。
这意味着该副作用将在组件上运行一次,卸载:
useEffect(() => {
// startTimer will run once on component mount
startTimer();
// The cleanup callback will run once on component unmount
return stopTimer;
}, []);
但是,就您而言,您永远不会卸载组件(因为在单击按钮时分派操作,这意味着您始终处于更新周期),请尝试在stopTimer
处设置一个断点。