К каким жизненным циклам компонента дает доступ UseEffect
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
useEffect и жизненные циклы React компонента
useEffect дает доступ ко всем основным фазам жизненного цикла компонента: монтирование (mount), обновление (update), демонтирование (unmount). Это главная причина, почему useEffect считается комбинацией componentDidMount, componentDidUpdate и componentWillUnmount.
Три фазы жизненного цикла
1. Монтирование (Mount) — компонент создаётся
useEffect(() => {
console.log('Компонент СМОНТИРОВАН');
// Этот код выполнится один раз после первого рендера
}, []); // Пустой массив зависимостей = только при монтировании
2. Обновление (Update) — props или state изменились
useEffect(() => {
console.log('userId изменился или компонент ререндерился');
// Этот код выполнится после КАЖДОГО рендера
}, [userId]); // Зависит от userId
3. Демонтирование (Unmount) — компонент удаляется из DOM
useEffect(() => {
return () => {
console.log('Компонент будет РАЗМОНТИРОВАН');
// Cleanup код
};
}, []);
Полный жизненный цикл компонента
import { useEffect, useState } from 'react';
export function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
// 1. МОНТИРОВАНИЕ
useEffect(() => {
console.log('1. Компонент смонтирован, userId:', userId);
return () => {
console.log('3. Компонент размонтирован');
};
}, []); // Только при первом монтировании
// 2. ОБНОВЛЕНИЕ (когда userId изменился)
useEffect(() => {
console.log('2. userId изменился на', userId);
setLoading(true);
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(data => {
setUser(data);
setLoading(false);
});
// Cleanup перед переходом на нового пользователя
return () => {
console.log('Cleanup: отмена загрузки для userId', userId);
// Отмена fetch запроса (AbortController)
};
}, [userId]); // Зависит от userId
if (loading) return <div>Загрузка...</div>;
return <div>{user?.name}</div>;
}
Варианты с разными зависимостями
1. Без массива зависимостей — выполняется после КАЖДОГО рендера
useEffect(() => {
console.log('Этот код выполнится после КАЖДОГО рендера');
});
// Эквивалент: componentDidMount + componentDidUpdate
2. Пустой массив [] — только при монтировании и размонтировании
useEffect(() => {
console.log('Монтирование');
return () => {
console.log('Размонтирование');
};
}, []);
// Эквивалент: componentDidMount + componentWillUnmount
3. С зависимостями [dep1, dep2] — когда зависимости изменились
useEffect(() => {
console.log('Очистка старого эффекта');
// Код эффекта
return () => {
console.log('Cleanup перед новым эффектом');
};
}, [dep1, dep2]);
// Эквивалент: componentDidUpdate (когда dep1 или dep2 изменились)
Практические примеры
Пример 1: Подписка на событие (типичный паттерн)
function ResizeListener() {
// МОНТИРОВАНИЕ: подписываемся
// ОБНОВЛЕНИЕ: ничего (нет зависимостей)
// РАЗМОНТИРОВАНИЕ: отписываемся
useEffect(() => {
const handleResize = () => {
console.log('Окно ресайзилось');
};
window.addEventListener('resize', handleResize);
// Cleanup: отписываемся
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // Только при монтировании/размонтировании
return <div>Слушаю resize</div>;
}
Пример 2: Таймер
function Timer({ seconds }) {
// МОНТИРОВАНИЕ: запускаем таймер
// ОБНОВЛЕНИЕ: отмена старого, запуск нового (если seconds изменился)
// РАЗМОНТИРОВАНИЕ: отмена таймера
useEffect(() => {
const interval = setInterval(() => {
console.log('Тик');
}, seconds * 1000);
return () => {
clearInterval(interval); // Cleanup
};
}, [seconds]);
return <div>Таймер работает</div>;
}
Пример 3: Загрузка данных
function Posts({ page }) {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// МОНТИРОВАНИЕ: загружаем posts для page 1
// ОБНОВЛЕНИЕ: загружаем posts для нового page
// РАЗМОНТИРОВАНИЕ: отмена запроса
const controller = new AbortController();
setLoading(true);
fetch(`/api/posts?page=${page}`, { signal: controller.signal })
.then(r => r.json())
.then(data => {
setPosts(data);
setLoading(false);
})
.catch(() => {
// Может ошибка от отмены (AbortError) — игнорируем
});
// Cleanup: отмена запроса
return () => {
controller.abort();
};
}, [page]); // Перезапускаем когда page изменился
return <div>{loading ? 'Загрузка' : posts.length}</div>;
}
Сравнение с классовыми компонентами
// Класс
class UserProfile extends React.Component {
componentDidMount() {
// МОНТИРОВАНИЕ
console.log('Смонтирован');
}
componentDidUpdate(prevProps) {
// ОБНОВЛЕНИЕ
if (prevProps.userId !== this.props.userId) {
console.log('userId изменился');
}
}
componentWillUnmount() {
// РАЗМОНТИРОВАНИЕ
console.log('Размонтирован');
}
}
// Функциональный компонент с useEffect
function UserProfile({ userId }) {
// МОНТИРОВАНИЕ + РАЗМОНТИРОВАНИЕ
useEffect(() => {
console.log('Смонтирован');
return () => console.log('Размонтирован');
}, []);
// ОБНОВЛЕНИЕ (когда userId изменился)
useEffect(() => {
console.log('userId изменился');
}, [userId]);
}
Важные моменты
-
Порядок выполнения:
- Рендер компонента
- Браузер рисует (paint)
- Выполняются эффекты (useEffect)
- Cleanup старых эффектов
-
Не блокирует рендер:
- useEffect асинхронно, не блокирует UI
- Для синхронных операций используй useLayoutEffect
-
Очистка (cleanup) важна:
- Отписывайся от событий
- Отменяй запросы
- Очищай таймеры
- Закрывай соединения
Итог
useEffect дает полный доступ к жизненному циклу:
- Монтирование → useEffect с пустыми зависимостями
- Обновление → useEffect с нужными зависимостями
- Размонтирование → return функция в useEffect
Это мощный инструмент, который заменяет все три метода класса в одном хуке.