当用户点击特定按钮时,计时器应该运行以计算用户执行任务的时间。计时器的格式应低于
HH : MM : SS
00 : 00 : 00
计时器应从0开始,1小时后结束。据我所知,这可以通过使用多个状态更新和setInterval
、setTimeout
、clearInterval
来实现。但我担心的是,如果我们连续运行多个状态更新,会有任何性能问题吗?
有没有任何react原生包可以实现这一点?非常感谢。
但我担心的是,如果我们连续运行多个状态更新,会有任何性能问题吗?
不应该,你说的是大约每秒钟更新一次,这并不算什么。
一定不要相信这个间隔,因为从你安排计时器到它启动之间的实际时间可能会根据其他情况而有很大的变化。因此,一定要始终计算初始启动时间的剩余时间,而不是你给计时器的时间的累积。例如:
timerCallback() {
this.setState({remaining: Date.now() - this.timerStarted});
}
或类似的。
以下是一个非常粗略的例子,说明为什么您总是重新计算,而不是从剩余时间中减去间隔:
function zeroPad(x, width) {
return String(x).padStart(width, "0");
}
class Time extends React.Component {
render() {
let time = Math.round(this.props.time / 1000);
const seconds = time % 60;
time -= seconds;
const minutes = Math.floor(time / 60) % 60;
time -= minutes * 60;
const hours = Math.floor(time / 3600);
return [
<span>{zeroPad(hours, 2)}</span>,
":",
<span>{zeroPad(minutes, 2)}</span>,
":",
<span>{zeroPad(seconds, 2)}</span>
];
}
}
class BadTimer extends React.Component {
constructor(props) {
super(props);
this.interval = 1000;
this.state = {
running: false,
remaining: 0,
actualDuration: 0
};
this.start = this.start.bind(this);
this.timerCallback = this.timerCallback.bind(this);
}
componentDidMount() {
if (this.props.autoStart) {
this.start();
}
}
start() {
this.setState(({running}) => {
if (!running) {
this.started = Date.now(); // Just so we can show actual duration later
setTimeout(this.timerCallback, this.interval);
return {running: true, remaining: this.props.length};
}
});
}
timerCallback() {
this.setState(({running, remaining}) => {
if (running) {
// Don't do this
remaining = Math.max(remaining - this.interval, 0);
if (remaining <= 500) {
return {running: false, remaining, actualDuration: Date.now() - this.started};
}
setTimeout(this.timerCallback, this.interval);
return {remaining};
}
});
}
render() {
const {running, remaining, actualDuration} = this.state;
return (
<span>
{running ? <Time time={remaining} /> : <input type="button" value="Start" onClick={this.start} />}
{actualDuration ? <span> Actual duration: <Time time={actualDuration} /></span> : false}
</span>
);
}
}
class GoodTimer extends React.Component {
constructor(props) {
super(props);
this.interval = 1000;
this.state = {
started: 0,
update: 0,
actualDuration: 0
};
this.start = this.start.bind(this);
this.timerCallback = this.timerCallback.bind(this);
}
componentDidMount() {
if (this.props.autoStart) {
this.start();
}
}
start() {
this.setState(({started, update}) => {
if (!started) {
setTimeout(this.timerCallback, this.interval);
started = Date.now();
return {started};
}
});
}
timerCallback() {
// Do this instead
this.setState(({started, update}) => {
if (started) {
const remaining = this.getRemaining(started);
if (remaining <= 500) {
return {started: 0, actualDuration: Date.now() - started};
}
setTimeout(this.timerCallback, this.interval);
return {update: update + 1};
}
});
}
getRemaining(started) {
return this.props.length - (Date.now() - started);
}
render() {
const {started, actualDuration} = this.state;
const remaining = this.getRemaining(started);
return (
<span>
{started ? <Time time={remaining} /> : <input type="button" value="Start" onClick={this.start} />}
{actualDuration ? <span> Actual duration: <Time time={actualDuration} /></span> : false}
</span>
);
}
}
ReactDOM.render(
<div>
<div>
<label>Bad:</label><BadTimer autoStart={true} length={15000} />
</div>
<div>
<label>Good:</label><GoodTimer autoStart={true} length={15000} />
</div>
</div>,
document.getElementById("root")
);
// Throw some delays in
for (let n = 3000; n < 12000; n += 3000) {
setTimeout(() => {
console.log("Thread is busy...");
const busy = Date.now();
setTimeout(() => {
// Do something that takes some time
const stop = Date.now() + (Math.random() * 2000) + 700;
while (Date.now() < stop) {
// Loop
}
console.log(`...done, was busy ${Date.now() - busy}ms`);
}, 0);
}, n);
}
body {
font-size: 16px;
}
label {
width: 48px;
display: inline-block;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>