Countdown timer not working properly in Next.js

Viewed 58

I want to create a Countdown timer in the next.js What I want to achieve is A timer that expires after let's say 4 HOURS. I have used setInterval function inside useEffect

It is affecting the component's other data they are also refreshing. refer https://www.loom.com/share/d902cf2010944f6b89462208237f195c

Here is what I have tried following

import { useState, useEffect } from "react";
import { endPoints } from "../../../../constant/endpoints";
import { randomNo } from "../../../../utils/functions";
import Ratings from "react-ratings-declarative";


function TodaysDeal(params) {
  const rattingValue = randomNo(3, 5);
  const [isLoading, setLoading] = useState(false);
  const [error, setError] = useState(false);
  const [products, setProducts] = useState([]);
  const [productsImages, setProductsImages] = useState([
    "https://dummyimage.com/400x400",
  ]);
  const [timeLeft, setTimeLeft] = useState({
    hours: "00",
    minutes: "00",
    seconds: "00",
  });

  useEffect(() => {
    setInterval(countDownTimer, 1000);
    setLoading(true);
    fetchProducts();
  }, []);

  const countDownTimer = () => {
    const difference = +new Date("2022-09-10T17:37:56+00:00") - +new Date();
    let timeLeft = {};
    console.log("difference: ", difference);
    if (difference > 0) {
      timeLeft = {
        hours: Math.floor(difference / (1000 * 60 * 60)),
        minutes: Math.floor((difference / 1000 / 60) % 60),
        seconds: Math.floor((difference / 1000) % 60),
      };
    }
    setTimeLeft(timeLeft);
    //return timeLeft;
  };

  const fetchProducts = async () => {
    try {
      const res = await fetch(`${endPoints.products}/${randomNo(1, 200)}`);
      const _data = await res.json();
      setProducts(_data);
      setProductsImages(_data.images);
      setLoading(false);
      console.log("deals of the day: ", _data);
    } catch (err) {
      console.log("try catch error logs: =====> ", err);
      setLoading(false);
      setError(true);
    }
  };

  return (
    <section className="pt-20 pb-20 body-font">
      <div className="container px-5 mx-auto uppercase font-bold tracking-widest">
        <div className="flex flex-wrap">
          <div className="md:w-3/5">
            <h1 className="text-gray-800 text-8xl display-block">
              Deal of the day
            </h1>
          </div>
          <div className="md:w-2/5">
            <h1 className="text-gray-800 text-8xl display-block text-center">
              <span>{timeLeft.hours}</span>
              <span>:</span>
              <span>{timeLeft.minutes}</span>
              <span>:</span>
              <span>{timeLeft.seconds}</span>
            </h1>
          </div>
        </div>
      </div>
      <section className="text-gray-600 body-font overflow-hidden">
        <div className="container px-5 py-10 mx-auto">
                  .
                  .
                  .
                  .
                  .
                  .
        </div>
      </section>
    </section>
  );
}

export default TodaysDeal;

Please correct me. Thanks!

2 Answers

reactStrictMode calls useEffect twice. So it might be making all those calls inside useEffect run twice. Try disabling it from next.config.js.

Correct way would be to check before making any async call. Preventing multiple calls. Or clear ongoing calls e.g clearInterval

It seems fetchProducts is being called more than once, even though inside the TodaysDeal component its being called only from useEffect(..,[]) equivalent to componentDidMount.

Hence it can be inferred that the TodaysDeal is destroyed mounted multiple times.

In any case, there needs to be a clearInterval.

useEffect(() => {
    var interval = setInterval(countDownTimer, 1000);
    setLoading(true);
    fetchProducts();
    return ()=> {
        // This function is run once when the component is unmounted
        // equivalent to componentWillUnmount
        clearInterval(interval);
    };
}, []);

edited:fix typo

Related