Какие вариации использования useEffect важны на практике и как их применять?
Хуки
useEffect — один хук, но паттернов его использования несколько, и на практике нужно знать, какой режим для какой задачи.
Эффект «на монтирование» — []
useEffect(() => {
analytics.pageView('/checkout');
}, []);Разовая инициализация: аналитика, первичная загрузка, установка глобальных слушателей. В StrictMode (dev, React 18+) выполнится дважды — код должен это переживать.
Эффект-синхронизация — [deps]
useEffect(() => {
let cancelled = false;
fetchUser(userId).then((u) => { if (!cancelled) setUser(u); });
return () => { cancelled = true; };
}, [userId]);Основной рабочий режим: перезагрузка данных при смене параметров, переподключение при смене канала. Cleanup защищает от гонок (race conditions) — ответ устаревшего запроса игнорируется.
Подписки с обязательным cleanup
useEffect(() => {
const handler = (e) => setWidth(e.target.innerWidth);
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);Каждой подписке — отписка, каждому таймеру — clearInterval, иначе утечки памяти и «мёртвые» обработчики.
Эффект после каждого рендера — без массива
Редкий случай: интеграции, которым нужно реагировать на любой рендер (например, синхронизация с императивной библиотекой). Чаще это признак ошибки.
Антипаттерны
- Вычисление данных из props/state через эффект + setState — лишний цикл рендера; считайте прямо в рендере или в useMemo.
- Обработка пользовательских действий в эффекте — место этому в обработчике события.
- «Цепочки» эффектов, триггерящих друг друга через state, — трудно отлаживать, лучше объединить логику.
Коротко для собеседования
Основные вариации: [] — разовая инициализация; [deps] — синхронизация с данными (обязательно с cleanup от гонок); подписки с отпиской в cleanup; без массива — почти никогда. Эффекты — для внешних систем, а не для вычислений из state.