> Как работает жизненный цикл React-компонентов (React)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: Инрэко, Точка, Kvando Technologies, ООО Премиум ИТ Солюшен, Kodix, Spotware, Aston, IT-One, EPAM, Сбер, SmartWay
Стек: React
> Пример ответа
Короткий ответ
Жизненный цикл React-компонентов - это последовательность фаз: монтирование, обновление и размонтирование. В классовых компонентах используются методы componentDidMount, componentDidUpdate, componentWillUnmount. В функциональных - useEffect с массивами зависимостей, который эмулирует эти фазы. React 18 с Concurrent Mode изменил приоритеты, но базовые фазы остались. Понимание цикла критично для управления побочными эффектами, оптимизации ререндеров и предотвращения утечек памяти.
Подробное объяснение
Жизненный цикл React-компонентов делится на три основные фазы:
-
Монтирование (Mounting) - компонент вставляется в DOM. В классовых: constructor → static getDerivedStateFromProps → render → componentDidMount. В функциональных: тело функции (инициализация state/refs) → return JSX → useEffect с пустыми зависимостями (аналог componentDidMount).
-
Обновление (Updating) - при изменении props или state. Классовые: getDerivedStateFromProps → shouldComponentUpdate → render → getSnapshotBeforeUpdate → componentDidUpdate. Функциональные: повторный вызов функции → useEffect с зависимостями (аналог componentDidUpdate, но с очисткой предыдущего эффекта).
-
Размонтирование (Unmounting) - удаление из DOM. Классовые: componentWillUnmount. Функциональные: return-функция внутри useEffect (cleanup).
Ключевые нюансы для senior:
- В строгом режиме (StrictMode) React дважды вызывает монтирование в dev для детекции side effects.
- Concurrent Mode позволяет React прерывать рендер, что ломает предположения о синхронности lifecycle.
useLayoutEffectсинхронно выполняется после мутаций DOM, но до отрисовки браузером - аналог componentDidMount/componentDidUpdate.- Error boundaries ловят ошибки в lifecycle методах (кроме event handlers и async code).
На практике
Для senior важно понимать trade-offs:
componentWillMount(устарел) - не используйте, переносите логику в constructor или componentDidMount.shouldComponentUpdateилиReact.memo- для оптимизации, но не злоупотребляйте, сравнивайте только при реальных проблемах с производительностью.getDerivedStateFromProps- редкий случай, обычно лучше lift state up или использовать key.- В функциональных компонентах избегайте бесконечных циклов в useEffect - проверяйте зависимости.
- Для подписок (WebSocket, event listeners) всегда используйте cleanup в useEffect, иначе утечки памяти.
Пример кода
JSX// Классовый компонентclass DataFetcher extends React.Component {state = { data: null, loading: true };componentDidMount() {this.fetchData();}componentDidUpdate(prevProps) {if (prevProps.url !== this.props.url) {this.fetchData();}}componentWillUnmount() {this.abortController?.abort();}fetchData() {this.abortController = new AbortController();fetch(this.props.url, { signal: this.abortController.signal }).then(res => res.json()).then(data => this.setState({ data, loading: false })).catch(err => {if (err.name !== 'AbortError') console.error(err);});}render() {return this.state.loading ? <Spinner /> : <div>{this.state.data}</div>;}}// Функциональный эквивалентfunction DataFetcher({ url }) {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => {const abortController = new AbortController();setLoading(true);fetch(url, { signal: abortController.signal }).then(res => res.json()).then(data => {setData(data);setLoading(false);}).catch(err => {if (err.name !== 'AbortError') console.error(err);});return () => abortController.abort();}, [url]);return loading ? <Spinner /> : <div>{data}</div>;}
Как отвечать на собеседовании
Начните с чёткого разделения классовых и функциональных подходов, упомяните Concurrent Mode как современный контекст. Покажите понимание не только фаз, но и практических проблем: утечки памяти, лишние ререндеры, race conditions в асинхронных запросах. Приведите пример, когда useLayoutEffect предпочтительнее useEffect (например, измерение DOM). Избегайте заучивания всех методов - сфокусируйтесь на ключевых и их заменах в хуках. Если спросят про StrictMode, объясните его назначение для выявления побочных эффектов.
Что проверяет интервьюер
- Глубину понимания lifecycle, а не поверхностное знание методов.
- Умение переключаться между классовым и функциональным подходами.
- Понимание Concurrent Mode и его влияния на lifecycle (например, что render может быть вызван несколько раз).
- Знание оптимизаций (shouldComponentUpdate, memo, useMemo) и их ограничений.
- Практический опыт с побочными эффектами, подписками и очисткой.
Типичные ошибки
- Путаница между
componentDidMountиuseEffectс пустыми зависимостями - забывают, что useEffect выполняется после каждого рендера, если не указать зависимости. - Игнорирование cleanup в useEffect - приводит к утечкам памяти и двойным запросам.
- Использование
getDerivedStateFromPropsбез необходимости - лучше управлять состоянием через key или lifting state up. - Предположение, что lifecycle методы синхронны в Concurrent Mode - React может отложить commit фазу.
- Забывают, что
componentWillUnmountне вызывается при ошибке в рендере - используйте error boundaries.
> Похожие задачи по frontend
Что такое React.memo и как он помогает оптимизировать рендеринг
Как работает useRef хук в React и для чего он используется
В чем отличие useEffect от useLayoutEffect в React
Как реализовать React компонент с текстовым полем для запроса к API, отображением результатов, индикатором загрузки и обработкой ошибок?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью