I have a React website where I have 2 toggles for different kinds of cards – one of them is Live markets (this type has a timer component).
Here is the problem- When I switch to classifieds and I switch back to live markets –
Auction timer for the first card becomes NaN. Note: this only happens to the first card, the other timers are fine.
I have a CardsLayout component, which send a request to the server for data when the above toggle is changed. And if it is in Live Markets tab, then the CardsLayout component maps each object to an AuctionCard component which has a Timer component inside it.
Here is the code for the Timer component-
import { useState, useEffect } from 'react';
export default function Timer({ id, endTime}) {
const [remainingTime, setRemainingTime] = useState(getRemainingTime());
useEffect(() => {
const interval = setInterval(() => {
setRemainingTime(getRemainingTime());
}, 1000);
return () => clearInterval(interval);
}, []);
function getRemainingTime() {
const now = new Date();
const end = new Date(endTime);
const diff = end.getTime() - now.getTime();
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff / (1000 * 60 * 60)) % 24);
const minutes = Math.floor((diff / (1000 * 60)) % 60);
const seconds = Math.floor((diff / 1000) % 60);
return { days, hours, minutes, seconds };
}
console.log('endtime-',id,endTime)
console.log('remtime-',id,remainingTime.seconds)
return (
<div className={remainingTime.seconds < 0 ? 'timer-ended' : 'timer'}>
{remainingTime.seconds < 0 ? (
"Auction over"
) : (
<>
Auction time remaining: {remainingTime.days} Days {remainingTime.hours}:
{remainingTime.minutes}:{remainingTime.seconds}
</>
)}
</div>
);
}
I also have 2 console statements. The values are getting printed every second. Here is whats getting printed for the Timer with NaN-
endtime- 4 2023-06-09T20:30:00.000Z
remtime- 4 NaN
2
Answers
Okay so the problem is handling of the data. The potential problem in your code is here. you need to console the diff before this line. diff can be 0 in a particular case. When diff will be 0 the statement written below will give you NAN
const seconds = Math.floor((diff / 1000) % 60);
Fix you can try
const seconds = diff ? Math.floor((diff / 1000) % 60) : 0 ;
Since it works after you add
id
andendTime
as dependencies to theuseEffect
(as mentioned in the comments of the OP), it seems that the issue is that the first render you do of the firstTime
is done without/or with a wrongendTime
so it end up displayingNaN
.Subsequent renders, I assume after fetching the data from somewhere, provide a valid
endTime
value for that property.Initially the change in that prop would not alter the functionality of the ongoing interval, since the
getRemainingTime
would refer to the initial value ofendTime
.There are a few solutions to this problem.
Timer
component until after you have valid data to provide to it. This need to be handled at the component using theTimer
and not inside it.key
for theTimer
component when using it (<Timer key={/*what ever you use for id will most likely work here too*/} id={..} endTime={..} />
that is unique and would change once you get the data from the remote location.useEffect
.For 3. the more correct approach is not to add
id
andendTime
to theuseEffect
but to useuseCallback
, withendTime
for thegetRemainingTime
and then use that as dependency for theuseEffect
.