在React中使用setState的setInterval

时间:2016-11-09 15:30:20

标签: javascript reactjs

我在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()的异步性质有关。另一个错误(我认为相关)是当我慢慢点击时,它只会每隔一段时间开始一次。

有人可以对此有所了解吗?或者他们采取了什么措施来绕过setStatesetInterval一起使用的异步问题(任何方式设置状态都可以返回一个promise?),或者哪种生命周期方法最适合这种情况?

2 个答案:

答案 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挂钩useStateuseEffect,您可以执行以下操作:

const [timer, setTimer] = useState(1);

useEffect(() => {
  const timerId = setInterval(() => setTimer(timer + 1), 1000);

  return () => clearInterval(timerId);
});