每秒更新反应组件

我一直在玩的反应,并有以下的时间分量,只是呈现在屏幕上的 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 的角度来看,让这个组件每秒钟更新一次以重新绘制时间的最佳方法是什么?

262390 次浏览

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

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

下面的代码是 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')
);

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

componentDidMount() {
this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
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>
);
}
}

所以你的方向是对的。在你的 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()函数,你不一定需要它,我的重构是100% 相当于用 constructor()函数初始化站点。

由于 React V16中的更改(其中组件 WillReceiveProps ()已被弃用) ,这是我用于更新组件的方法。 请注意,下面的示例使用的是类型脚本,并且在更新 Props 时,使用静态 getDeridStateFromProps 方法获取初始状态和更新状态。

    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>;
}
}

@ Waisky 建议:

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

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

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


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

关于评论:

您不需要在 []中传递任何内容。如果你在括号中传递 time,这意味着每次 time的值变化时都要运行效果,也就是说,每次 time变化时都会调用一个新的 setInterval,这不是我们要查找的。我们希望只在组件挂载时调用 setInterval一次,然后 setInterval每1000秒调用 setTime(Date.now())一次。最后,在卸载组件时调用 clearInterval

请注意,每次 time的值发生变化时,组件都会根据您在其中使用 time的方式进行更新。这与将 time放入 useEffect[]无关。

我自己喜欢 setTimeout多于 setInterval,但没有找到一个解决方案的类为基础的组件。你可以在基于类的组件中使用类似的东西:

基于类的组件和 setInterval:

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
};
}


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


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


tick() {
this.setState({
date: new Date()
});
}


render() {
return (
this.state.date.toLocaleTimeString()
);
}
}


ReactDOM.render(
<Clock / > ,
document.getElementById('app')
);
<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>


<div id="app" />

基于函数的组件和 setInterval:

Https://codesandbox.io/s/sweet-diffie-wsu1t?file=/src/index.js

基于函数的组件和 setTimeout:

Https://codesandbox.io/s/youthful-lovelace-xw46p