React应用中的setInterval


101

我在React还是很新,但是我一直在慢慢地学习,遇到了一些我坚持的事情。

我正在尝试在React中构建一个“计时器”组件,说实话,我不知道我是否做得正确(或有效)。在下面的代码中,我将状态设置为返回对象,{ currentCount: 10 }并且一直在与componentDidMount,玩弄componentWillUnmountrender并且我只能使状态从10倒数到9。

分两部分的问题:我怎么了?而且,有没有一种更有效的方式来使用setTimeout(而不是使用componentDidMountcomponentWillUnmount)?

先感谢您。

import React from 'react';

var Clock = React.createClass({

  getInitialState: function() {
    return { currentCount: 10 };
  },

  componentDidMount: function() {
    this.countdown = setInterval(this.timer, 1000);
  },

  componentWillUnmount: function() {
    clearInterval(this.countdown);
  },

  timer: function() {
    this.setState({ currentCount: 10 });
  },

  render: function() {
    var displayCount = this.state.currentCount--;
    return (
      <section>
        {displayCount}
      </section>
    );
  }

});

module.exports = Clock;

2
bind(this)不再需要,React现在可以自行执行。
德里克·波拉德

2
您的计时器方法不会更新currentCount
Bryan Chen

1
@Derek确定吗?我只是通过添加this.timer.bind(this)this.timer使其工作了
蠕虫病毒

6
@Theworm @Derek是错误的,有点。React.createClass(不建议使用)class Clock extends Component会自动绑定方法,但不会自动绑定。因此,是否需要绑定取决于您如何创建组件。
CallMeNorm

Answers:


157

我发现您的代码有4个问题:

  • 在计时器方法中,您始终将当前计数设置为10
  • 您尝试在render方法中更新状态
  • 您没有使用setState方法来实际更改状态
  • 您没有在状态中存储intervalId

让我们尝试解决该问题:

componentDidMount: function() {
   var intervalId = setInterval(this.timer, 1000);
   // store intervalId in the state so it can be accessed later:
   this.setState({intervalId: intervalId});
},

componentWillUnmount: function() {
   // use intervalId from the state to clear the interval
   clearInterval(this.state.intervalId);
},

timer: function() {
   // setState method is used to update the state
   this.setState({ currentCount: this.state.currentCount -1 });
},

render: function() {
    // You do not need to decrease the value here
    return (
      <section>
       {this.state.currentCount}
      </section>
    );
}

这将导致计时器从10减少到-N。如果您希望计时器减少到0,则可以使用稍微修改的版本:

timer: function() {
   var newCount = this.state.currentCount - 1;
   if(newCount >= 0) { 
       this.setState({ currentCount: newCount });
   } else {
       clearInterval(this.state.intervalId);
   }
},

谢谢。这很有道理。我仍然还是一个初学者,我试图掌握状态的工作方式以及渲染等“块”的内容。
何塞

我想知道,是否有必要使用componentDidMount和componentWillUnmount来实际设置间隔?编辑:刚刚看到了您最近的编辑。:)
何塞

@Jose我认为componentDidMount是触发客户端事件的正确位置,因此我将使用它来启动倒计时。您还在考虑采用什么其他方法进行初始化?
dotnetom,2016年

我没有什么特别的主意,但是在组件中使用这么多的“块”似乎很笨拙。我想这只是我自己习惯于React中的零碎工作。再次谢谢你!
何塞

4
真正不需要将setInterval值存储为状态的一部分,因为它不会影响渲染
Gil

32

使用了更新的10秒倒计时 class Clock extends Component

import React, { Component } from 'react';

class Clock extends Component {
  constructor(props){
    super(props);
    this.state = {currentCount: 10}
  }
  timer() {
    this.setState({
      currentCount: this.state.currentCount - 1
    })
    if(this.state.currentCount < 1) { 
      clearInterval(this.intervalId);
    }
  }
  componentDidMount() {
    this.intervalId = setInterval(this.timer.bind(this), 1000);
  }
  componentWillUnmount(){
    clearInterval(this.intervalId);
  }
  render() {
    return(
      <div>{this.state.currentCount}</div>
    );
  }
}

module.exports = Clock;

20

更新了使用Hooks的 10秒倒计时(一种新的功能建议,使您无需编写类即可使用状态和其他React功能。它们当前在React v16.7.0-alpha中)。

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

const Clock = () => {
    const [currentCount, setCount] = useState(10);
    const timer = () => setCount(currentCount - 1);

    useEffect(
        () => {
            if (currentCount <= 0) {
                return;
            }
            const id = setInterval(timer, 1000);
            return () => clearInterval(id);
        },
        [currentCount]
    );

    return <div>{currentCount}</div>;
};

const App = () => <Clock />;

ReactDOM.render(<App />, document.getElementById('root'));

在React 16.8中,React Hooks可以稳定发布。
Greg Herbowicz

2

谢谢@dotnetom,@ greg-herbowicz

如果返回“ this.state未定义”-绑定计时器函数:

constructor(props){
    super(props);
    this.state = {currentCount: 10}
    this.timer = this.timer.bind(this)
}

2

如果有人在寻找实现SetInterval的React Hook方法。丹·阿布拉莫夫(Dan Abramov)在博客上谈到了这个问题。如果您想很好地了解该主题(包括“课堂”方法),请检查一下。基本上,代码是一个自定义的Hook,它将setInterval变为声明性的。

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

为了方便起见,还发布了CodeSandbox链接:https ://codesandbox.io/s/105x531vkq


0

在React类中每秒更新一次状态。请注意,我的index.js传递了一个返回当前时间的函数。

import React from "react";

class App extends React.Component {
  constructor(props){
    super(props)

    this.state = {
      time: this.props.time,

    }        
  }
  updateMe() {
    setInterval(()=>{this.setState({time:this.state.time})},1000)        
  }
  render(){
  return (
    <div className="container">
      <h1>{this.state.time()}</h1>
      <button onClick={() => this.updateMe()}>Get Time</button>
    </div>
  );
}
}
export default App;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.