React-приложения строятся из компонентов, и у каждого компонента есть своя жизнь. Он появляется на экране. Реагирует на новую информацию. В конце концов исчезает. Игнорируйте этот ритм — и цена будет вполне реальной. Появляются утечки памяти. Производительность рендеринга падает. Пользователи нажимают на кнопки, которые больше не отвечают, потому что компонент, привязанный к ним, размонтировался десять минут назад, но обработчик событий всё еще жив. Понимание жизненного цикла компонента — это не академическое упражнение. Это фундамент написания программного обеспечения, которое остается надежным в реальных условиях эксплуатации.

Представьте существование компонента в три этапа. Разработчики часто описывают их как рождение, рост и смерть. React называет их mounting, updating и unmounting. У каждого этапа есть своя задача, и каждый предоставляет точный момент для выполнения кода или его очистки.

Три фазы

Mounting — это рождение. Компонент создается и впервые вставляется в браузерный DOM. Это ваше окно для настройки. Если компоненту нужны удаленные данные, именно здесь вы их запрашиваете. Если ему нужно начальное состояние, рассчитанное на основе props, вы делаете это здесь. В эпоху классовых компонентов это было областью ответственности componentDidMount. В современных функциональных компонентах вы управляете этим с помощью useEffect и пустого массива зависимостей. Ключевая деталь заключается в том, что этот блок кода выполняется один раз, сразу после того, как React помещает элемент на страницу. Это делает его идеальным для разовой настройки, такой как получение профиля пользователя или чтение значения из localStorage.

Updating — это рост. Смонтированный компонент не стоит на месте. Приходят props от родителя. Состояние (state) меняется в результате взаимодействия с пользователем. Сам родитель перерисовывается, заставляя дочерний компонент следовать за ним. Затем React перерисовывает компонент, чтобы интерфейс (UI) оставался синхронизированным с вашими данными. Эта фаза повторяется многократно в течение жизни компонента. Каждый раз, когда меняется зависимость, ваши эффекты запускаются снова, и компонент вдыхает новый результат обратно в DOM. Поскольку эта фаза запускается так часто, именно здесь чаще всего начинаются проблемы с производительностью. Лишняя работа на этом этапе быстро накапливается.

Unmounting — это смерть. Компонент исчезает с экрана, возможно, потому, что пользователь перешел на другую страницу или условный рендеринг (conditional render) его отключил. В этот момент компонент уничтожается, но внешний мир не всегда об этом знает. Таймеры продолжают тикать. WebSockets продолжают слушать. Обработчики событий (event listeners) на объекте window остаются привязанными. Если вы не очистите их, они будут удерживать ссылки на «мертвые» компоненты и вызывать утечку памяти. Браузер будет тратить циклы процессора на призраков. React предоставляет вам функцию очистки (cleanup function) именно для того, чтобы вы могли остановить интервалы, закрыть соединения и удалить обработчики до того, как компонент исчезнет навсегда.

От методов классов к хукам

Годами инженеры React управляли этими фазами через классовые компоненты. Логика была разделена по различным методам. Вы получали данные внутри componentDidMount, реагировали на изменения props внутри componentDidUpdate и «подметали пол» внутри componentWillUnmount. Проблема заключалась в том, что связанная логика оказывалась разбросанной. Одна и та же функция, например, подключение к чату, требовала кода в трех разных методах жизненного цикла только для того, чтобы подключиться, проверить изменения в ветке и отключиться.

Хуки изменили это. Хук useEffect в функциональных компонентах позволяет группировать логику по назначению, а не по фазе жизненного цикла. Тем не менее, ментальная модель легко сопоставляется, если знать, куда смотреть.

Для обработки монтирования (mounting) пишите useEffect(() => { ... }, []). Пустой массив зависимостей говорит React, что у этого эффекта нет внешних зависимостей, поэтому он запускается только после начального рендеринга. Это заменяет componentDidMount.

Для обработки обновлений (updates) укажите конкретные значения, за которыми должен следить React: useEffect(() => { ... }, [dependency]). Когда dependency меняется между рендерингами,