Reputation: 1657
When user tap on particular button Timer should run to calculate how long the user performing the task. Timer should be in below format
HH : MM : SS
00 : 00 : 00
Timer should start from 0, end after 1 hour. As per my knowledge this can be achieved by using multiple state updates and setInterval
, setTimeout
, clearInterval
. But my concern is will there be any performance issue if we run multiple state updates on continuous basis?
Is there any react-native package to achieve this? Thank You!
Upvotes: 1
Views: 499
Reputation: 1074148
But my concern is will there be any performance issue if we run multiple state updates on continuous basis?
There shouldn't be, you're talking about an update every second or so, which isn't all that much.
Be sure not to trust the interval, since the actual time between when you schedule a timer and when it fires can vary widely based on what else is going on. So be sure to always calculate the time remaining from the initial start time, not an accumulation of the times you've given the timer. E.g.:
timerCallback() {
this.setState({remaining: Date.now() - this.timerStarted});
}
or similar.
Here's a very rough example of why you always recalc rather than subtracting the interval from the time remaining:
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>
Upvotes: 1