Search code examples
reactjsreact-hookscountdown

Hooks countdown timer does not show zeros


Countdown timer does not show zeros values. When seconds or minutes approaching zero, timer delete span, where showing value. If timer time 7 minutes 00 seconds he shows 7, but i need minutes 07 seconds 00

Time used React hooks.

Code

function calculateTimeLeft() {


const year = new Date().getFullYear();
  const difference = +new Date(`${year}-10-1`) - +new Date();
  let timeLeft = {};

  if (difference > 0) {
    timeLeft = {
      days: Math.floor(difference / (1000 * 60 * 60 * 24)),
      hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
      minutes: Math.floor((difference / 1000 / 60) % 60),
      seconds: Math.floor((difference / 1000) % 60)
    };
  }

  return timeLeft;
}
export default function App() {
  const [timeLeft, setTimeLeft] = React.useState(calculateTimeLeft());

  React.useEffect(() => {
    const id = setTimeout(() => {
      setTimeLeft(calculateTimeLeft());
    }, 1000);

    return () => {
      clearTimeout(id);
    };
  });

  const timerComponents = Object.keys(timeLeft).map(interval => {
    if (!timeLeft[interval]) {
      return;
    }

    return (
      <span>
        {timeLeft[interval]} {interval}{" "}
      </span>
    );
  });

  return (
    <div>
      {timerComponents.length ? timerComponents : <span>Time's up!</span>}
    </div>
  );
}

Solution

  • This check prevent the rendering of the interval for every falsy value, which means that 0 won't be rendered as well:

    if (!timeLeft[interval]) {
      return;
    }
    

    You should check if the value is undefined to skip rendering, although it's redundant your case. An empty object would produce an empty array of keys, which means that the forEach callback won't be invoked.

    if (timeLeft[interval] === undefined) {
      return;
    }
    

    Demo:

    function calculateTimeLeft() {
      const year = new Date().getFullYear();
      const difference = +new Date(`${year}-10-1`) - +new Date();
      let timeLeft = {};
    
      if (difference > 0) {
        timeLeft = {
          days: Math.floor(difference / (1000 * 60 * 60 * 24)),
          hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
          minutes: Math.floor((difference / 1000 / 60) % 60),
          seconds: Math.floor((difference / 1000) % 60)
        };
      }
    
      return timeLeft;
    }
    function App() {
      const [timeLeft, setTimeLeft] = React.useState(calculateTimeLeft());
    
      React.useEffect(() => {
        const id = setTimeout(() => {
          setTimeLeft(calculateTimeLeft());
        }, 1000);
    
        return () => {
          clearTimeout(id);
        };
      });
    
      const timerComponents = Object.keys(timeLeft).map(interval => {
        if (timeLeft[interval] === undefined) {
          return;
        }
    
        return (
          <span key={interval}>
            {timeLeft[interval]} {interval}{" "}
          </span>
        );
      });
    
      return timerComponents;
    }
    
    ReactDOM.render(
      <App />,
      root
    );
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    
    <div id="root"></div>

    Another option is to avoid creating all timerComponents if timeLeft is null (instead of setting an empty object):

    function calculateTimeLeft() {
      const year = new Date().getFullYear();
      const difference = +new Date(`${year}-10-1`) - +new Date();
    
      return difference > 0 ?
        {
          days: Math.floor(difference / (1000 * 60 * 60 * 24)),
          hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
          minutes: Math.floor((difference / 1000 / 60) % 60),
          seconds: Math.floor((difference / 1000) % 60)
        } : null;
    }
    function App() {
      const [timeLeft, setTimeLeft] = React.useState(calculateTimeLeft());
    
      React.useEffect(() => {
        const id = setTimeout(() => {
          setTimeLeft(calculateTimeLeft());
        }, 1000);
    
        return () => {
          clearTimeout(id);
        };
      });
      
      if(!timeLeft) return null;
    
      return Object.keys(timeLeft).map(interval => (
        <span key={interval}>
          {timeLeft[interval]} {interval}{" "}
        </span>
      ));
    }
    
    ReactDOM.render(
      <App />,
      root
    );
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    
    <div id="root"></div>