skip to Main Content

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).

enter image description here

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.

enter image description here

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


  1. 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 ;

    Login or Signup to reply.
  2. Since it works after you add id and endTime as dependencies to the useEffect (as mentioned in the comments of the OP), it seems that the issue is that the first render you do of the first Time is done without/or with a wrong endTime so it end up displaying NaN.

    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 of endTime.

    There are a few solutions to this problem.

    1. Do not render the Timer component until after you have valid data to provide to it. This need to be handled at the component using the Timer and not inside it.
    2. Provide a key for the Timer 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.
    3. Use correct dependencies for the useEffect.

    For 3. the more correct approach is not to add id and endTime to the useEffect but to use useCallback, with endTime for the getRemainingTime and then use that as dependency for the useEffect.

    const getRemainingTime = useCallback(function() {
      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 };
    }, [endTime]);
    
    useEffect(() => {
      const interval = setInterval(() => {
        setRemainingTime(getRemainingTime());
      }, 1000);
    
      return () => clearInterval(interval);
    }, [getRemainingTime]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search