我在React组件中有一个使用setInterval()
的计时器,我不确定在使用state
时启动和停止此间隔的最佳做法是什么。我遇到了一些异步问题。
假设我的React组件中有一组链接,可以很好地呈现和执行回调:
let links = [10, 50, 100, 500, 1000].map((num) => {
return(
<Link key={num} onClick={(e) => this.switchNums(num)} to={`/somePath/${num}`}>{num}</Link>
)
})
这是switchNums()
函数,我希望它重置现有的计时器:
switchNums(num){
this.stopTimer()
this.reset(num)
}
以下是startTimer()
,stopTimer()
和reset()
:
startTimer(){
if(!this.state.timerId){
let timerId = setInterval(()=>{
let timer = this.state.timer + 1
this.setState({
timer: timer,
timerId: timerId
})
}, 1000)
}
}
stopTimer(){
clearInterval(this.state.timerId)
this.setState({timerId:null})
}
reset(size){
this.setState({
gameOver: false,
counter: 0,
correct: 0,
numbers: this.getRandomNumbers(size),
timer: 0
}, this.startTimer())
}
其中一个错误是快速点击链接会导致多个间隔在if
{{}}} startTimer()
条件下触发。我猜这与setState()
的异步性质有关。另一个错误(我认为相关)是当我慢慢点击时,它只会每隔一段时间开始一次。
有人可以对此有所了解吗?或者他们采取了什么措施来绕过setState
与setInterval
一起使用的异步问题(任何方式设置状态都可以返回一个promise?),或者哪种生命周期方法最适合这种情况?
答案 0 :(得分:8)
我认为这里最大的缺陷是你使用state
来存储你的间隔。虽然技术上可行,但我认为没有理由你真的想要这样做。
相反,只需对组件使用局部变量:
startTimer(){
if(!this.timerId){
this.timerId = setInterval(()=>{
//your function
}, 1000);
}
}
stopTimer(){
clearInterval(this.timerId);
}
所以我认为您根本不需要使用state
作为计时器。您的帖子中有其他一般性问题虽然与state
有关,但我会尝试回答以下问题。请记住,它们与解决您的特定问题无关。
他们采取了哪些措施来规避setState()
的异步问题?
在设置state
之后,您可以使用回调来执行代码。这有一个section of the official docs;这就是它所说的:
第二个参数是一个可选的回调函数,它将在setState完成并重新呈现组件后执行。
setState(nextState, callback);
哪种生命周期方法最适合此类情况?
上述文档的相同部分仍在继续:
通常我们建议使用componentDidUpdate()代替这种逻辑。
如果您的函数中有多个setState
,并且您希望在特定事件后执行特定代码,我认为您可以使用回调。为了更一般的目的,使用上面的生命周期方法。
答案 1 :(得分:0)
使用React挂钩useState
和useEffect
,您可以执行以下操作:
const [timer, setTimer] = useState(1);
useEffect(() => {
const timerId = setInterval(() => setTimer(timer + 1), 1000);
return () => clearInterval(timerId);
});