При повторном запуске таймера, он накладывается на предыдущий

Есть реактовский функциональный компонент, в нём есть таймер обратного отсчёта, который запускается по клику на кнопку. Оставшееся время выводится в html элемент, проблема в том, что при повторном клике на кнопку, запускается второй таймер обратного отсчёта и 2 таймера работают параллельно, о чём свидетельствует вывод в html.

Я хочу, что бы при запуске функции с таймером, проводилась проверка, не запущен-ли предыдущий таймер, если запущен - остановить прошлый и начать отсчёт заново, если не запущен - запустить

import React from "react";
import "./styles.css";

export default function App() {
  const timer = (remainingMinutes, d, h, m, s) => {
    var finishTime = new Date();
    finishTime.setSeconds(finishTime.getSeconds() + remainingMinutes);
    let timeLeft = document.getElementById("timer");
    var timesOver;

    function update() {
      // сколько осталось миллисекунд
      var diff = finishTime - new Date();
      // сколько миллисекунд до конца секунды
      var millis = diff % 1000;
      diff = Math.floor(diff / 1000);
      // сколько секунд до конца минуты
      var sec = diff % 60;
      if (sec < 10) sec = "0" + sec;
      diff = Math.floor(diff / 60);
      // сколько минут до конца часа
      var min = diff % 60;
      if (min < 10) min = "0" + min;
      diff = Math.floor(diff / 60);
      // сколько часов до конца дня
      var hours = diff % 24;
      if (hours < 10) hours = "0" + hours;
      var days = Math.floor(diff / 24);

      d = +days;
      h = +hours;
      m = +min;
      s = +sec;

      timeLeft.innerHTML = `Offer is valid for ${m} minutes ${s} seconds`;

      // возвращаем оставшееся колличество секунд
      timesOver = d * 86400 + h * 3600 + m * 60 + s;

      // Если время закончилось - выводим заглушку
      if (timesOver === 0) {
        return (timeLeft.innerHTML = `Offer timed out`);
      }

      // следующий раз вызываем себя, когда закончится текущая секунда
      setTimeout(update, millis);
    }
    setTimeout(update, 0);
  };
  return (
    <div className="App">
      <h4 id="timer">TIMER</h4>
      <button onClick={() => timer(30)}>Start Timer </button>
    </div>
  );
}

Наглядный пример. Если кликнуть по кнопке раз 5 подряд, видно, что время в заголовке начинает "прыгать"


Ответы (1 шт):

Автор решения: Евгений Платов
  let temp = 0;
  const timer = (remainingMinutes, d, h, m, s) => {
    var finishTime = new Date();
    finishTime.setSeconds(finishTime.getSeconds() + remainingMinutes);
    let timeLeft = document.getElementById("timer");
    var timesOver;

    function update() {
      // сколько осталось миллисекунд
      var diff = finishTime - new Date();
      // сколько миллисекунд до конца секунды
      var millis = diff % 1000;
      diff = Math.floor(diff / 1000);
      // сколько секунд до конца минуты
      var sec = diff % 60;
      if (sec < 10) sec = "0" + sec;
      diff = Math.floor(diff / 60);
      // сколько минут до конца часа
      var min = diff % 60;
      if (min < 10) min = "0" + min;
      diff = Math.floor(diff / 60);
      // сколько часов до конца дня
      var hours = diff % 24;
      if (hours < 10) hours = "0" + hours;
      var days = Math.floor(diff / 24);

      d = +days;
      h = +hours;
      m = +min;
      s = +sec;

      timeLeft.innerHTML = `Offer is valid for ${m} minutes ${s} seconds`;

      // возвращаем оставшееся колличество секунд
      timesOver = d * 86400 + h * 3600 + m * 60 + s;

      // Если время закончилось - выводим заглушку
      if (timesOver === 0) {
        return (timeLeft.innerHTML = `Offer timed out`);
      }

      // следующий раз вызываем себя, когда закончится текущая секунда
      clearTimeout(temp);
      temp = setTimeout(update, millis);
    }
    setTimeout(update, 0);
  };
→ Ссылка