setState和Callback

时间:2019-03-25 21:17:49

标签: reactjs

我正在编写一个简单的番茄计时器,只需单击一下按钮即可将其触发。

 tick() {
    if (this.state.minutes > 0) {
      setInterval(() => {
        this.timer(this.state.minutes * 60 + this.state.seconds - 1);
      }, 1000);
    }
  }
  timer(x) {
    this.setState(
      {
        minutes: Math.trunc(x),
        seconds: (x*60) % 60
      },
      () => this.tick()
    );
  }

  render() {
    return (
      <div className="App">
        <div className="Pomodo">
          <div className="counter">
            {this.state.minutes + ":" + this.state.seconds}
          </div>
          <button className="btnCode" onClick={() => this.timer(25)}>
            Code
          </button>
          <button className="btnCoffee" onClick={() => this.timer(5)}>
            Coffee
          </button>
          </div>
      </div>
    );
  }
}

export default App;

这显示计时器如下:

25:00(正确)

24:59(正确)

24:57 ==错误应该是58

24:53 ==错误应该是57

...等

我在这里想念什么?通过Chrome进行故障排除时,计数器可以正常显示正确的数字。

1 个答案:

答案 0 :(得分:1)

之所以跳过秒,是因为每次调用tick()时,您都需要通过调用this.tick()中的setState来设置新的间隔。您可以通过在首次调用tick之后添加一个标志来解决此问题。

我看到的另一个问题是,您在btnCode的onClick中将25作为分钟传递给this.timer。但是,在setInterval中,您将以秒为单位调用计时器。我建议您以秒为单位将所有内容传递给计时器功能。另外,最好在卸载时清除间隔。

请参阅调整后的代码。

class TodoApp extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
    	minutes: 0,
      seconds: 0,
      started: false
    }
    this.tick = this.tick.bind(this);
    this.timer = this.timer.bind(this);
  }
  
   tick() {
   // Add flag so that tick is not called again
   this.setState({
    started: true
   })
   
    if (this.state.minutes > 0) {
      this.interval = setInterval(() => {
       const seconds = this.state.minutes * 60 + this.state.seconds - 1;
        this.timer(seconds);
      }, 1000);
    }
  }
  componentWillUnmount() {
    clearInterval(this.interval);
  }
  timer(x) {
    this.setState(
      {
        minutes: Math.trunc(x / 60),
        seconds: x % 60
      },
      () => !this.state.started && this.tick() // only call if its timer is not started before
    );
  }

  render() {
    return (
      <div className="App">
        <div className="Pomodo">
          <div className="counter">
            {this.state.minutes + ":" + this.state.seconds}
          </div>
          <button className="btnCode" onClick={() => this.timer(25 * 60)}>
            Code
          </button>
          <button className="btnCoffee" onClick={() => this.timer(5 * 60)}>
            Coffee
          </button>
          </div>
      </div>
    );
  }

}

ReactDOM.render(<TodoApp />, document.querySelector("#app"))
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>