Очистка эффекта
Некоторые эффекты оставляют после себя что-то работающее: таймер, обработчик события окна, подписку. Если компонент исчезнет со страницы, а таймер продолжит тикать, получится утечка — код работает впустую и может менять состояние несуществующего компонента. Чтобы этого не было, эффект возвращает функцию очистки.
Таймер с очисткой
Создайте src/components/Clock.jsx — часы, которые обновляются каждую секунду:
// src/components/Clock.jsx
import { useEffect, useState } from 'react';
export default function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(timer);
}, []);
return <p>Сейчас {time.toLocaleTimeString()}</p>;
}Выведите <Clock /> в App — время на странице будет меняться каждую секунду. Функция, которую вернул эффект, останавливает таймер.
Когда React вызывает очистку
| Момент | Что происходит |
|---|---|
| Компонент исчезает со страницы | Вызывается очистка |
| Зависимость изменилась | Сначала очистка старого эффекта, потом запуск нового |
Разработка со StrictMode | Запуск, очистка и снова запуск сразу после появления |
Проверка на странице
Добавьте в App кнопку, которая показывает и прячет часы:
// src/App.jsx
import { useState } from 'react';
import Clock from './components/Clock.jsx';
export default function App() {
const [showClock, setShowClock] = useState(true);
return (
<>
<button onClick={() => setShowClock(!showClock)}>Часы</button>
{showClock && <Clock />}
</>
);
}Когда часы спрятаны, компонента Clock нет на странице, и его таймер остановлен. Без очистки таймер продолжил бы работать, а в консоли могли бы появиться ошибки.
Обработчик события окна
Так же снимают обработчики, добавленные через addEventListener. Компонент показывает ширину окна — src/components/WindowWidth.jsx:
// src/components/WindowWidth.jsx
import { useEffect, useState } from 'react';
export default function WindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
function handleResize() {
setWidth(window.innerWidth);
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return <p>Ширина окна: {width}px</p>;
}Меняйте размер окна браузера — число на странице будет обновляться. В removeEventListener передают ту же функцию, что и в addEventListener.
Что нужно очищать
| Что запускает эффект | Чем очищать |
|---|---|
setInterval, setTimeout | clearInterval, clearTimeout |
addEventListener | removeEventListener |
| Подписка на внешний источник | Отписка, которую он предоставляет |
Запрос fetch | Флаг «ответ больше не нужен», см. урок про загрузку данных |
Частая ошибка
Без очистки в режиме разработки эффект с setInterval запустится дважды, и счётчик будет расти на 2 в секунду вместо 1:
// Неправильно: таймер никогда не останавливается.
useEffect(() => {
setInterval(() => setSeconds((value) => value + 1), 1000);
}, []);Если StrictMode выявил двойную работу, это знак, что эффекту не хватает функции очистки.