Reputation: 75
I have a functional component that is supposed to be a running clock:
import React,{useState,useEffect} from 'react';
import 'materialize-css/dist/css/materialize.min.css';
import { parseTime } from '../../Utils/utils'
const MainClock = (props) => {
const [timeString, setTimeString] = useState(parseTime(new Date(), true));
function tick(){
console.log("TICK:" + timeString)
setTimeString(parseTime(new Date(), true));
};
useEffect(()=>{console.log("rendered!");setTimeout(tick,500);},[timeString]);
return (
<div>
<h5 className="center-align mainclock">{timeString}</h5>
</div>
);
}
export default MainClock;
But for some reason it is only being rendered twice and the console output is:
rendered!
TICK:14:56:21
rendered!
TICK:14:56:22
Why isn't useeffect being called after the second render?
Any help is welcomed!
Edit: If it helps, this is parseTime
:
const parseTime = (timeDate, withSeconds=false) =>{
let time = timeDate.getHours()<10 ? `0${timeDate.getHours()}`:`${timeDate.getHours()}`;
time+=":";
time+= timeDate.getMinutes()<10 ? `0${timeDate.getMinutes()}`:`${timeDate.getMinutes()}`;
if(withSeconds){
time+=":";
time+=timeDate.getSeconds()<10 ? `0${timeDate.getSeconds()}`:`${timeDate.getSeconds()}`;
}
return time;
}
Upvotes: 4
Views: 140
Reputation: 29354
Problem is the use of setTimeout
and the use of low delay, i.e 500ms
for the timeout. If you log the return value of parseTime
, you will notice that between the two calls, it returns the same time string, so state never updates, leading to the component never re-rendering and hence useEffect
never executes again to set another setTimeout
.
Increase the timeout delay or check the return value of the parseTime
function and if its the same as the one in the state, call this function again.
Moreover, its more appropriate to use setInterval
here instead of setTimeout
because setInterval
will only need to be called once and it will call the tick
function repeatedly until the interval is cancelled. If you use setTimeout
, then you will need to call setTimeout
again and again to schedule a new tick
function call.
Upvotes: 4
Reputation: 8751
As I indicated above, it is the problem of the short setTimeout
timing value - 500ms
.
To make it work, you need to use setInterval
const MainClock = (props) => {
const [timeString, setTimeString] = useState(parseTime(new Date(), true));
function tick(){
console.log("TICK:" + timeString)
setTimeString(parseTime(new Date(), true));
};
useEffect(() => {
setInterval(tick, 500);
}, []);
useEffect(()=>{console.log("rendered!");},[timeString]);
return (
<div>
<h5 className="center-align mainclock">{timeString}</h5>
</div>
);
}
Upvotes: 2