useEffect: побочные эффекты
Побочный эффект — это действие, которое выходит за пределы рендера: запрос к API, подписка на событие, изменение заголовка страницы, работа с localStorage.
useEffect выполняет такие действия после рендера.
Синтаксис
import { useEffect } from 'react';
useEffect(() => {
// эффект
}, [зависимости]);
Первый аргумент — функция с эффектом. Второй — массив зависимостей.
Эффект при монтировании
Пустой массив зависимостей — эффект выполнится один раз:
useEffect(() => {
console.log('Компонент смонтирован');
}, []);
Эффект при изменении
Если указать зависимости — эффект срабатывает при их изменении:
useEffect(() => {
console.log('count изменился:', count);
}, [count]);
Эффект после каждого рендера
Без массива — после каждого рендера:
useEffect(() => {
console.log('рендер');
});
Используется редко.
Очистка
Если эффект создаёт подписку — нужна функция очистки:
useEffect(() => {
const id = setInterval(() => {
console.log('тик');
}, 1000);
return () => clearInterval(id);
}, []);
Функция, возвращаемая из эффекта, вызывается:
- при размонтировании компонента
- перед повторным запуском эффекта
Запрос к API
function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
async function load() {
setLoading(true);
const res = await fetch(`/api/users/${userId}`);
const data = await res.json();
if (!cancelled) {
setUser(data);
setLoading(false);
}
}
load();
return () => {
cancelled = true;
};
}, [userId]);
if (loading) return <p>Загрузка...</p>;
return <p>{user?.name}</p>;
}
cancelled защищает от гонки: если userId изменился, старый запрос не перезапишет новое состояние.
Подписка на событие
useEffect(() => {
function handleResize() {
console.log(window.innerWidth);
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
Изменение заголовка
useEffect(() => {
document.title = `Сообщений: ${count}`;
}, [count]);
Зависимости
React сравнивает зависимости по ссылке. Объекты и функции — новые при каждом рендере:
useEffect(() => {
//
}, [{ id: 1 }]); // будет срабатывать каждый рендер
Решение — мемоизация через useMemo и useCallback.
useEffect не для вычислений
Если значение можно вычислить из пропсов или стейта — не используй useEffect:
// Плохо
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(price * quantity);
}, [price, quantity]);
// Хорошо
const total = price * quantity;
Итоги
useEffectвыполняет побочные эффекты после рендера- Пустой массив — один раз при монтировании
- Зависимости — массив значений, при изменении которых эффект сработает
- Функция очистки отменяет подписки и запросы
- Не используй
useEffectдля вычисляемых значений