Не работает clearInterval
const Timer = () => {
var timerId = 0;
let [timer, setTimer] = useState(10);
useEffect(() => {
if (timer === 0) {
clearInterval(timerId);
}
}, [timer]);
timerId = setInterval(updateCoutDown, 1000);
function updateCoutDown() {
setTimer((timer = timer - 1));
}
return (
<div>
<h1>{timer}</h1>
</div>
);
};
Проблема заключается в том что clearInterval не работает. Читал похожие проблемы, ничего не помогло
Ответы (3 шт):
Попробуйте так. У Вас переменная timerId - постоянно обнулялась и соответсвенно была проблема. Код немного поправил
import React, {useState, useEffect, useRef} from 'react';
const Timer = () => {
const timerId = useRef();
const [timer, setTimer] = useState(10);
useEffect(()=>{
timerId.current = setInterval(() =>
setTimer(prev => prev-1)
, 1000);
}, []);
useEffect(() => {
if (timer === 0) {
clearInterval(timerId.current);
}
}, [timer]);
return (
<div>
<h1>{timer}</h1>
</div>
);
};
clearInterval - работает. Просто запускается много setInterval, а останавливается только один.
Чтобы запускался только один, его можно запускать внутри useEffect с пустым массивом зависимостей.
В этом случае, однако, id таймера нужно хранить в стейте, чтобы можно было остановить его не только в момент выгрузки компонента
const {
useState,
useEffect
} = React;
const Timer = () => {
var [timerId, setTimerId] = useState(0);
let [timer, setTimer] = useState(10);
useEffect(() => {
if (timer === 0) {
clearInterval(timerId);
console.log('timer stop');
}
}, [timer, timerId]);
useEffect(() => {
let internalTimerId = setInterval(updateCoutDown, 1000);
function updateCoutDown() {
setTimer(prev => prev - 1);
console.log('timer hit');
}
setTimerId(internalTimerId)
return () => clearInterval(internalTimerId);
}, [])
return ( <
div >
<
h1 > {
timer
} < /h1> <
/div>
);
};
ReactDOM.createRoot(root).render( < Timer / > )
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Замечания:
- Не стоит использовать
var, вместо него используйтеlet - НИ В КОЕМ СЛУЧАЕ не мутируйте стейт компонента. Чтобы избежать такого рода ошибок стейты объявляют через
const, а неlet - Если внутри
useEffect-а используете таймеры, то не забывайте прописывать очищение таймера при удалении компонента
Решения:
На самом деле в вашей задаче нет никакой нужды в
setInterval-е. Задача намного легче решается если использоватьsetTimeout:Пишем
useEffect, который будет слушатьtimerВнутри него каждый раз объявляем новый
setTimeout, который очищаем сразу при достижении0
Плюс такого подхода в том, что нам не нужно думать об очищении таймера после каждой секунды. Мы следим только за
timer-омconst {useState, useEffect} = React; const Timer = () => { const [timer, setTimer] = useState(10); useEffect(() => { const timerId = setTimeout(updateCountDown, 1000); if (timer === 0) clearTimeout(timerId); return () => clearTimeout(timerId); }, [timer]); function updateCountDown() { setTimer(timer => timer - 1); } return ( <div> <h1>{timer}</h1> </div> ); }; ReactDOM.createRoot(root).render(<Timer />);<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script> <div id="root"></div>Если вам всё же необходимо использовать именно
setInterval, тогда можно сделать так:Объявляем
timerIdс помощьюuseRef, потому что нам не нужно будет его менять между перерисовками. Всё что нам нужно - это сохранить идентификатор таймера даже после перерисовкиПишем
useEffect, который запустим лишь один раз - при монтировании компонента. Таким образом мы избегам случая, когда у нас создаётся множество таймеровДалее остаётся лишь проверять наш счётчик. При достижении
0мы очищаем таймер
const {useState, useEffect, useRef} = React; const Timer = () => { const timerId = useRef(null); const [timer, setTimer] = useState(10); useEffect(() => { timerId.current = setInterval(updateCountDown, 1000); return () => clearTimeout(timerId.current); }, []); if (timer === 0) clearTimeout(timerId.current); function updateCountDown() { setTimer(timer => timer - 1); } return ( <div> <h1>{timer}</h1> </div> ); }; ReactDOM.createRoot(root).render(<Timer />);<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script> <div id="root"></div>