Жизненный цикл компонента
Жизненный цикл компонента — последовательность фаз от создания (mounting) до удаления (unmounting), в которые React выполняет рендеринг, обновление и очистку.
Зачем нужно
Понимание жизненного цикла объясняет когда использовать useEffect и с какими зависимостями, почему нужна функция очистки (cleanup) и как избежать утечек памяти. Без этого знания разработчик пишет эффекты вслепую и получает баги: лишние запросы к API, незакрытые подписки, обновление размонтированного компонента.
Где используется
- Подписки на WebSocket, EventSource при монтировании → отписка при размонтировании
- Загрузка данных при монтировании и при изменении пропсов
- Интеграция со сторонними библиотеками (карты, графики)
- Таймеры:
setIntervalпри монтировании →clearIntervalпри размонтировании
Три фазы жизненного цикла
Mounting (монтирование)
→ Компонент создаётся и вставляется в DOM
→ useEffect с — аналог componentDidMount
Updating (обновление)
→ Props или state изменились → ре-рендер
→ useEffect с зависимостями — аналог componentDidUpdate
Unmounting (размонтирование)
→ Компонент удаляется из DOM
→ Cleanup функция в useEffect — аналог componentWillUnmount
useEffect — хук жизненного цикла
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [status, setStatus] = useState('online');
// 1. Эффект при монтировании + при изменении userId
useEffect(() => {
let cancelled = false; // флаг для предотвращения race condition
setUser(null); // сброс перед новым запросом
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(data => {
// Не обновляем state если компонент уже размонтирован
if (!cancelled) setUser(data);
});
// Cleanup: сигнализируем об отмене при смене userId или размонтировании
return => { cancelled = true; };
}, [userId]); // [userId] = запускать при изменении userId
// 2. Эффект с WebSocket — подписка/отписка
useEffect(() => {
const ws = new WebSocket(`wss://api.example.com/status/${userId}`);
ws.onmessage = (event) => {
setStatus(event.data);
};
// Cleanup: закрываем соединение при размонтировании
return => ws.close();
}, [userId]);
// 3. Эффект без зависимостей — один раз при монтировании
useEffect(() => {
document.title = 'Профиль пользователя';
return => {
document.title = 'Приложение'; // сброс при размонтировании
};
}, ); // = только при монтировании/размонтировании
if (!user) return <p>Загрузка...</p>;
return (
<div>
<h1>{user.name}</h1>
<span>Статус: {status}</span>
</div>
);
}
Порядок выполнения в React
1. render ← React вычисляет виртуальный DOM
2. DOM update ← React обновляет реальный DOM
3. useLayoutEffect ← синхронно после обновления DOM (редко)
4. useEffect ← асинхронно после отрисовки браузером
При обновлении:
old cleanup ← сначала cleanup предыдущего эффекта
render
DOM update
new effect ← затем новый эффект
Частые ошибки
- Нет зависимостей в
useEffect— без массива зависимостей эффект запускается после каждого рендера; добавляйте зависимости явно. - Нет cleanup функции — подписки, таймеры, WebSocket-соединения не закрываются → утечки памяти и ошибки после размонтирования.
- Обновление state размонтированного компонента — при асинхронных запросах нужен флаг отмены или AbortController.
- Бесконечный цикл — если внутри
useEffectизменяется state, который включён в зависимости → цикл.
Связанные темы
- _MOC SPA
- State -- внутреннее состояние
- Компонент -- что это такое
- Загрузка данных и loading states
- Error Boundaries