clearInterval не работает при использовании setState React.js

Я создаю таймер обратного отсчета с помощью React и столкнулся с этим: clearInterval работает onCklick, если он не обновляет компонент. Если я обновляю состояние в интервале, функция clearInterval больше не очищает интервал. Я так понимаю, что это связано с перерисовкой компонента? Вот соответствующая часть кода. Советы очень ценятся:

const [sessionCount, setSessionCount] = useState(25);
const [timeLeft, setTimeLeft] = useState("25:00")

  
var timer ;
 
function startHandle() {
    let endTime = (sessionCount * 60000) + +new Date();
    timer = setInterval(function() {
        let diff = endTime - +new Date();
        const min = Math.floor(diff / 60000);
        const sec = Math.floor((diff / 1000) % 60);
        setTimeLeft(min + ":" + sec) //if this line is removed, then clearInterval works
    }, 1000)
};
function resetHandle() {
  setTimeLeft("25:00");
  clearInterval(timer);
};
<div id="time-display-container">
    <div id="timer-label">Session</div>
    <time id="time-left"></time>
    <button id="start_stop" onClick={startHandle}>start/stop</button>
    <button id="reset" onClick={resetHandle}>reset</button>
</div>

person keijop    schedule 25.01.2021    source источник
comment
При повторном рендеринге timer не сохраняет свое последнее значение. Лучше сохраните его в ref, используя useRef, чтобы сохранить его значения между повторными рендерингами.   -  person Rohit Khanna    schedule 25.01.2021
comment
Возможный дубликат stackoverflow .com/questions/57137094/   -  person Giovanni Esposito    schedule 25.01.2021
comment
@ Джованни Эспозито, спасибо, кажется, я плохо разбирался в уже заданных вопросах. Я попытаюсь исправить это с помощью ref и useEffect, чтобы лучше реагировать.   -  person keijop    schedule 25.01.2021
comment
@Rohit Khanna, спасибо, я попробую и useRef, и useEffect.   -  person keijop    schedule 25.01.2021


Ответы (1)


Переместите таймер за пределы компонента приложения.

import React, { useState } from "react";

let timer;


export default function App() {
  const [sessionCount, setSessionCount] = useState(25);
  const [timeLeft, setTimeLeft] = useState("25:00");


  function startHandle() {
    let endTime = sessionCount * 60000 + +new Date();
    timer = setInterval(function () {
      let diff = endTime - +new Date();
      const min = Math.floor(diff / 60000);
      const sec = Math.floor((diff / 1000) % 60);
      setTimeLeft(min + ":" + sec); //if this line is removed, then clearInterval works
    }, 1000);
  }

  function resetHandle() {
    setTimeLeft("25:00");
    clearInterval(timer);
  }

  return (
    <div id="time-display-container">
      <div id="timer-label">Session</div>
      <time id="time-left" />
      {timeLeft}
      <button id="start_stop" onClick={startHandle}>
        start/stop
      </button>
      <button id="reset" onClick={resetHandle}>
        reset
      </button>
    </div>
  );
}

также вы проверяете значение таймера, чтобы предотвратить установку нескольких заданных интервалов. Я предполагаю, что определение этого таймера в useEffect и вызов их в каком-либо другом изменении состояния было бы более реактивным, но приведенный выше код исправление работал для меня в codesandbox

person Amirhossein Ebrahimi    schedule 25.01.2021