useEffect: подробно
useEffect выполняет побочные эффекты после рендера. Это запросы к API, подписки, работа с DOM, таймеры.
Синтаксис
useEffect(() => {
// эффект
return () => {
// очистка (опционально)
};
}, [зависимости]);
Три режима
Без зависимостей
useEffect(() => {
console.log('после каждого рендера');
});
Выполняется после каждого рендера. Редко нужен.
Пустой массив
useEffect(() => {
console.log('только при монтировании');
}, []);
Выполняется один раз после первого рендера.
С зависимостями
useEffect(() => {
console.log('count изменился');
}, [count]);
Выполняется при монтировании и при каждом изменении зависимостей.
Функция очистки
Возвращаемая функция вызывается:
- перед повторным запуском эффекта
- при размонтировании компонента
useEffect(() => {
const id = setInterval(() => console.log('тик'), 1000);
return () => clearInterval(id);
}, []);
Запрос к API
useEffect(() => {
let cancelled = false;
async function load() {
const res = await fetch(`/api/users/${userId}`);
const data = await res.json();
if (!cancelled) {
setUser(data);
}
}
load();
return () => {
cancelled = true;
};
}, [userId]);
cancelled защищает от гонки: если userId изменился, старый запрос не перезапишет состояние.
Подписка на событие
useEffect(() => {
function handleScroll() {
console.log(window.scrollY);
}
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
Работа с DOM
useEffect(() => {
document.title = `Сообщений: ${count}`;
}, [count]);
Зависимости и ссылки
React сравнивает зависимости по ссылке. Объекты и функции — новые при каждом рендере:
// Будет срабатывать каждый рендер
useEffect(() => {
//
}, [{ id: 1 }]);
Решение:
- Для объектов —
useMemo - Для функций —
useCallback - Или вынести объект за пределы компонента
Пропуск эффекта
Можно пропустить эффект, вернув undefined:
useEffect(() => {
if (!userId) return;
// запрос
}, [userId]);
useEffect не для всего
Не используй useEffect для:
- Вычисляемых значений — считай их прямо в теле компонента
- Синхронизации между стейтами — используй один стейт
- Обработки событий — используй обработчики
// Плохо
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(price * quantity);
}, [price, quantity]);
// Хорошо
const total = price * quantity;
useEffect и StrictMode
В dev-режиме React монтирует компоненты дважды, чтобы выявить проблемы с очисткой. Это нормально: эффект выполнится дважды, очистка тоже дважды.
Итоги
useEffectвыполняет эффекты после рендера- Зависимости определяют, когда эффект сработает
- Очистка отменяет подписки и запросы
- Не используй
useEffectдля вычислений - В dev-режиме эффект может срабатывать дважды