每秒更新React组件


114

我一直在玩React,并具有以下仅显示Date.now()在屏幕上的时间组件:

import React, { Component } from 'react';

class TimeComponent extends Component {
  constructor(props){
    super(props);
    this.state = { time: Date.now() };
  }
  render(){
    return(
      <div> { this.state.time } </div>
    );
  }
  componentDidMount() {
    console.log("TimeComponent Mounted...")
  }
}

export default TimeComponent;

从React的角度来看,使该组件每秒更新以重新绘制时间的最佳方法是什么?

Answers:


150

您需要使用setInterval来触发更改,但是还需要在组件卸载时清除计时器,以防止它留下错误和内存泄漏:

componentDidMount() {
  this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000);
}
componentWillUnmount() {
  clearInterval(this.interval);
}

2
如果您想要一个封装此类内容的库,我做了react-interval-rerender
Andy

我在构造函数中添加了setInterval方法,效果更好。
aqteifan

89

以下代码是React.js网站的修改示例。

原始代码在这里可用:https//reactjs.org/#a-simple-component

class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      seconds: parseInt(props.startTimeInSeconds, 10) || 0
    };
  }

  tick() {
    this.setState(state => ({
      seconds: state.seconds + 1
    }));
  }

  componentDidMount() {
    this.interval = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  formatTime(secs) {
    let hours   = Math.floor(secs / 3600);
    let minutes = Math.floor(secs / 60) % 60;
    let seconds = secs % 60;
    return [hours, minutes, seconds]
        .map(v => ('' + v).padStart(2, '0'))
        .filter((v,i) => v !== '00' || i > 0)
        .join(':');
  }

  render() {
    return (
      <div>
        Timer: {this.formatTime(this.state.seconds)}
      </div>
    );
  }
}

ReactDOM.render(
  <Timer startTimeInSeconds="300" />,
  document.getElementById('timer-example')
);

1
由于某种原因,我使用此方法时得到this.updater.enqueueSetState不是函数错误。setInterval回调已正确绑定到此组件。
baldrs

16
对于像我这样的白痴:不要命名计时器,updater因为它会破坏更新周期
baldrs

3
使用ES6,我需要像这样更改一行。interval = setInterval(this.tick.bind(this),1000);
卡皮尔

您可以将链接添加到引用的网址吗?谢谢
〜– frederj

我添加了格式化方法和“构造函数”属性,以提高鲁棒性。
Polywhirl先生

15

@Waisky建议:

您需要使用setInterval来触发更改,但是还需要在组件卸载时清除计时器,以防止它留下错误和内存泄漏:

如果您想做同样的事情,请使用Hooks:

const [time, setTime] = useState(Date.now());

useEffect(() => {
  const interval = setInterval(() => setTime(Date.now()), 1000);
  return () => {
    clearInterval(interval);
  };
}, []);

关于评论:

您无需在内部传递任何内容[]。如果time放在方括号中,则表示每次time更改值都运行效果,即setInterval每次time更改都调用一个新值,这不是我们想要的。我们只想setInterval在安装组件时调用一次,然后每1000秒setInterval调用setTime(Date.now())一次。最后,我们clearInterval在组件卸载时调用。

请注意time,每次time更改值时,组件都会根据您的使用方式进行更新。这已无关把time[]useEffect


1
为什么将空数组([])作为第二个参数传递给useEffect
Mekeor Melire

阵营的影响钩文档告诉我们:“如果你想运行的效果和清理一次(在装载和卸载),你可以传递一个空数组([])作为第二个参数。” 但是OP希望效果每秒运行一次,即重复发生,而不只是一次。
Mekeor Melire

如果您在数组中传入[time],我注意到的差异将在每个时间间隔内创建和销毁组件。如果传递空数组[],它将继续刷新组件,仅在离开页面时才将其卸载。但是,无论哪种情况,要使其正常工作,我都必须添加key = {time}或key = {Date.now()}才能使其真正重新呈现。
Teebu

8

在组件的componentDidMount生命周期方法中,您可以设置间隔来调用更新状态的函数。

 componentDidMount() {
      setInterval(() => this.setState({ time: Date.now()}), 1000)
 }

4
正确,但是如最佳答案所示,请记住清除时间以避免内存泄漏:componentWillUnmount(){clearInterval(this.interval); }
亚历山大·福尔克

3
class ShowDateTime extends React.Component {
   constructor() {
      super();
      this.state = {
        curTime : null
      }
    }
    componentDidMount() {
      setInterval( () => {
        this.setState({
          curTime : new Date().toLocaleString()
        })
      },1000)
    }
   render() {
        return(
          <div>
            <h2>{this.state.curTime}</h2>
          </div>
        );
      }
    }

0

所以您走在正确的轨道上。在您内部,您componentDidMount()可以通过实现setInterval()触发更改来完成工作,但是请记住,通过来更新组件状态的方法是通过setState(),因此在您内部,您componentDidMount()可以这样做:

componentDidMount() {
  setInterval(() => {
   this.setState({time: Date.now()})    
  }, 1000)
}

而且,您可以使用上面提供Date.now()componentDidMount()实现方式的哪一种有效,但是您将获得一堆难以理解的讨厌数字更新,但是从技术上讲,这是自1970年1月1日以来每毫秒更新一次的时间,但是想要使这段时间对我们人类阅读时间的方式具有可读性,因此除了学习和实现之外,setInterval您还想要学习new Date()toLocaleTimeString(),您将像这样实现它:

class TimeComponent extends Component {
  state = { time: new Date().toLocaleTimeString() };
}

componentDidMount() {
  setInterval(() => {
   this.setState({ time: new Date().toLocaleTimeString() })    
  }, 1000)
}

注意,我也删除了该constructor()函数,您不一定需要它,我的重构相当于使用该constructor()函数初始化站点的100%。


0

由于已弃用componentWillReceiveProps()的React V16中的更改,这就是我用来更新组件的方法。请注意,下面的示例在Typescript中,并且在更新Props时使用静态getDerivedStateFromProps方法获取初始状态和更新状态。

    class SomeClass extends React.Component<Props, State> {
  static getDerivedStateFromProps(nextProps: Readonly<Props>): Partial<State> | null {
    return {
      time: nextProps.time
    };
  }

  timerInterval: any;

  componentDidMount() {
    this.timerInterval = setInterval(this.tick.bind(this), 1000);
  }

  tick() {
    this.setState({ time: this.props.time });
  }

  componentWillUnmount() {
    clearInterval(this.timerInterval);
  }

  render() {
    return <div>{this.state.time}</div>;
  }
}
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.