Reputation: 5235
I'm starting to learn React and I want to create a component that compares the current time to starting time and update the view.
Here's how I make it:
import React from 'react';
const Timer = ({ callQueuedTime }) => (
setInterval(function () {
console.log("test");
return <p>{new Date().getTime() - new Date(callQueuedTime).getTime()}</p>
}, 1000)
)
export default Timer;
In devTools, the console.log
is refreshing every 1 sec but not the HTML. Also, I'm always getting the same value, which is not the value of new Date().getTime() - new Date(callQueuedTime).getTime()
For information callQueuedTime= 2020-04-23T22:02:07.382Z
UPDATE
I have updated my component code:
class Timer extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
timer: 0,
}
}
componentDidMount() {
console.log(this.props)
setInterval(function () {
const time = new Date().getTime() - new Date(this.props.callQueuedTime).getTime()
this.setState({ time });
}, 1000)
}
render() {
return (
<p>{this.state.timer}</p>
);
}
}
Now, after the first second, I get this error:
Cannot read property 'callQueuedTime' of undefined
Upvotes: 2
Views: 3534
Reputation: 11017
You can definitely do this with a functional component.
As noted, you can't return JSX from a setInterval call. setInterval returns an id to use to clear.
The way I go about this is to keep the time as state, and then update that time every second in an effect.
import React, { useEffect, useState } from 'react';
const Timer = ({ callQueuedTime }) => {
const [time, setTime] = useState(() => new Date().getTime());
useEffect(() => {
const queuedTime = new Date(callQueuedTime).getTime();
const intervalId = setInterval(function () {
setTime(new Date().getTime() - queuedTime);
}, 1000);
return ()=>{
clearInterval(intervalId);
}
}, [callQueuedTime]);
return <p>{time}</p>;
};
export default Timer;
The function being returned in the useEffect
is the cleanup function. It is called before the useEffect runs the next time and before the component unmounts.
For further reading:
https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup
https://overreacted.io/a-complete-guide-to-useeffect/
Here's the working code as a class component:
The issue you were running into was that you were using a function in the setInterval rather than an arrow function, so you were losing access to your component's this
. The other issue you would run into is that you weren't cleaning up your interval on unmount.
import React, { Component } from "react";
export default class TimerComponent extends React.Component {
state = { time: 0 };
componentDidMount() {
this.intervalId = setInterval(() => {
const time =
new Date().getTime() - new Date(this.props.callQueuedTime).getTime();
this.setState({ time });
}, 1000);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
render() {
return <p>{this.state.time}</p>;
}
}
Here's a stackblitz showcasing both options:
https://stackblitz.com/edit/react-w2gqa6?file=Timer.jsx
Upvotes: 3