setInterval with setState in React

setInterval with setState in React

本文关键字:React in setState with setInterval      更新时间:2023-09-26

我在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())
}

其中一个bug是快速点击链接会导致多个间隔触发,尽管startTimer()中的if条件。我猜这与setState()的异步特性有关。另一个错误(我认为相关)是,当我慢慢点击,它只开始间隔每隔一次。

有人能解释一下吗?或者他们做了什么来规避setStatesetInterval一起使用的异步问题(任何方式设置状态都可以返回承诺?),或者哪种生命周期方法最适合这种类型的情况?

我认为这里最大的缺陷是您使用state来存储您的间隔。虽然技术上是可行的,但我看不出你为什么要这样做。

相反,只需在组件中使用一个局部变量:

startTimer(){
  if(!this.timerId){     
    this.timerId = setInterval(()=>{
      //your function
    }, 1000);
  }
}
stopTimer(){
  clearInterval(this.timerId);
}

所以我认为你不需要在这里使用state定时器。你在你的帖子中还有一些与state有关的一般性问题,我将在下面回答这些问题。只要记住,它们与解决你的问题是无关的。


他们做了什么来规避setState()的异步问题?

在设置了state之后,可以使用回调来执行代码。官方文档中有一节是关于这个的;它是这样写的:

第二个参数是一个可选的回调函数,它将在setState完成并且组件被重新渲染后执行。

setState(nextState, callback);

哪种生命周期方法最适合这种情况?

文档的相同部分继续:

通常我们建议使用componentDidUpdate()来代替这种逻辑。

如果你的函数中有多个setState,并且你想在特定事件后执行特定代码,我认为你可以使用回调。对于更一般的用途,请使用上面的生命周期方法。

使用React Hooks useStateuseEffect,你可以做以下事情:

const [timer, setTimer] = useState(1);
useEffect(() => {
  const timerId = setInterval(() => setTimer(timer + 1), 1000);
  return () => clearInterval(timerId);
});