Как остановить setTimeout вызвав функцию остановки stop() в useEffect?
Как остановить setTimeout вызвав функцию остановки stop() в useEffect?
function App() {
const [value, setValue] = useState(0)
const [isStart, setIsStart] = useState(false)
useEffect(() => {
stop()
}, [value])
let timeoutId = null;
function start() {
console.log('start')
if (timeoutId) {
return;
}
timeoutId = setTimeout(() => {
console.log('timeout')
setIsStart(true)
}, 5000);
}
function stop() {
if (timeoutId) {
console.log('stop')
setIsStart(false)
clearTimeout(timeoutId);
timeoutId = null;
}
}
return (
<div className="App">
<button onClick={() => setValue(value + 1)}>value+1</button>
<button onClick={() => start()}>OK</button>
<div>
{`value: ${value}`}
</div>
<div>
{isStart ? 'Start' : 'Nope'}
</div>
</div>
);
}
export default App;
Ответы (1 шт):
Для того, чтобы у вас успешно получилось остановить timeout в useEffect, при изменении какого-то состояния, сам timeoutId должен быть сохранен не просто в виде переменной, а в виде ref'a react. Для этого в функциональной компоненте можно использовать хук useRef.
Ваш код работал некорректно, только лишь потому, что вы теряли ссылку на timeoutId после рендера компонента. Все дело в том, что когда рендерится функциональный реакт компонент весь код внутри него выполняется снова и снова, т.е. если просто положить значение внутри компонента в переменную const someValue = {val: 1} - то это значение будет присваиваться каждый рендер новое
Ссылка на раздел документации о useRef: https://ru.reactjs.org/docs/hooks-reference.html#useref Ссылка на вопросы о переменных в реакт компонентах: https://ru.reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables
Исправленный вариант кода:
function App() {
const [value, setValue] = useState(0);
const [isStart, setIsStart] = useState(false);
useEffect(() => {
stop();
}, [value]);
/**
* Для сохранения значения в переменной
* между рендерами компонента, можно использовать useRef.
* Таким образом, мы не потеряем, при обновлени компонента,
* данные о timeout
*/
let timeoutRef = useRef(null);
function start() {
console.log("start");
if (timeoutRef.current) {
return;
}
timeoutRef.current = setTimeout(() => {
console.log("timeout");
setIsStart(true);
}, 5000);
}
function stop() {
if (timeoutRef.current) {
console.log("stop");
setIsStart(false);
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
}
return (
<div className="App">
<button onClick={() => setValue(value + 1)}>value+1</button>
<button onClick={() => start()}>OK</button>
<div>{`value: ${value}`}</div>
<div>{isStart ? "Start" : "Nope"}</div>
</div>
);
}