Как реализовать жизненные циклы компонентов с помощью useEffect?
Хуки
Все три фазы жизненного цикла классового компонента выражаются одним хуком useEffect — разными комбинациями массива зависимостей и cleanup-функции.
componentDidMount → useEffect(fn, [])
useEffect(() => {
loadInitialData();
}, []); // один раз после монтированияПустой массив: эффект выполнится после первого рендера и больше не перезапустится.
componentDidUpdate → useEffect(fn, [deps])
useEffect(() => {
loadUser(userId); // при монтировании И при смене userId
}, [userId]);Отличие от классов: не нужно вручную сравнивать prevProps.userId !== this.props.userId — сравнение зависимостей делает React. Если нужен эффект строго после каждого рендера — массив опускается совсем.
componentWillUnmount → cleanup-функция
useEffect(() => {
const timer = setInterval(tick, 1000);
return () => clearInterval(timer); // при размонтировании
}, []);Всё вместе
useEffect(() => {
const ws = subscribe(channelId, onMessage); // mount + update по channelId
return () => ws.close(); // cleanup перед перезапуском и на unmount
}, [channelId]);Отличия от классовой модели
- Cleanup вызывается не только при размонтировании, но и перед каждым перезапуском эффекта — модель «синхронизации», а не «этапов жизни».
- Логика группируется по смыслу (подписка + отписка рядом), а не разнесена по трём методам.
- Аналога shouldComponentUpdate у useEffect нет — это задача React.memo.
- Синхронного аналога componentWillMount нет (и он не нужен); для чтения DOM до отрисовки есть useLayoutEffect.
Коротко для собеседования
useEffect(fn, []) = componentDidMount; useEffect(fn, [deps]) = componentDidUpdate по конкретным данным; cleanup-функция = componentWillUnmount (плюс вызывается перед каждым перезапуском). Хуки группируют связанную логику вместе вместо разнесения по методам класса.