← Назад к вопросам

Расскажи про жизненный цикл функциональных компонентов React

2.0 Middle🔥 261 комментариев
#React#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Жизненный цикл функциональных компонентов React: от хуков до обновлений

В современном React с использованием функциональных компонентов концепция "жизненного цикла" перестала быть строго последовательной, как в классовых компонентах (mount → update → unmount). Вместо этого мы управляем состоянием и побочными эффектами через хуки (Hooks), которые позволяют "подключаться" к различным фазам существования компонента. Основные этапы можно описать следующим образом.

Фазы существования функционального компонента

  1. Монтирование (Mounting) – компонент впервые добавляется в DOM.
  2. Обновление (Updating) – компонент перерисовывается из-за изменения пропсов, состояния или контекста.
  3. Размонтирование (Unmounting) – компонент удаляется из DOM.

Ключевые хуки для управления жизненным циклом

useState и useReducer отвечают за состояние, но сами не являются "жизненным циклом". Они просто хранят данные между рендерами. Основную работу по реакции на фазы выполняют useEffect и useLayoutEffect.

useEffect: основной инструмент для побочных эффектов

Хук useEffect позволяет выполнять код после того, как компонент отрендерился и изменения были зафиксированы в DOM. Его поведение зависит от передаваемого массива зависимостей.

import { useEffect } from 'react';

function MyComponent({ userId }) {
  useEffect(() => {
    // Этот код выполнится после каждого рендера, если массив зависимостей пуст
    console.log('Компонент отрендерился в DOM');
  });

  useEffect(() => {
    // Этот код выполнится только при монтировании и размонтировании
    console.log('Эффект на монтирование (аналог componentDidMount)');

    return () => {
      console.log('Эффект на размонтирование (аналог componentWillUnmount)');
    };
  }, []); // Пустой массив зависимостей

  useEffect(() => {
    // Этот код выполнится при монтировании и при каждом изменении userId
    fetchUserData(userId);
    
    return () => {
      // Очистка предыдущего эффекта перед новым выполнением или размонтированием
      abortFetch();
    };
  }, [userId]); // Массив с зависимостью userId
}
  • Пустой массив зависимостей []: эффект выполняется только один раз после монтирования, а функция очистки (return) – при размонтировании.
  • Массив с зависимостью [userId]: эффект выполняется после монтирования и после каждого обновления, если userId изменился. Функция очистки выполняется перед следующим выполнением этого эффекта или перед размонтировании.
  • Отсутствие массива: эффект выполняется после каждого рендера (монтирования и всех обновлений). Это может быть неэффективно.

useLayoutEffect: эффект перед отображением пользователю

Хук useLayoutEffect имеет одинаковую сигнатуру с useEffect, но выполняется синхронно сразу после вычисления DOM-обновлений, но перед тем, как браузер отобразит их пользователю. Это полезно для измерений DOM или мутаций, которые должны быть видимы пользователю сразу.

import { useLayoutEffect, useRef } from 'react';

function Tooltip() {
  const ref = useRef();
  
  useLayoutEffect(() => {
    // Измеряем размеры элемента перед отображением
    const { height } = ref.current.getBoundingClientRect();
    // Немедленно применяем стиль, чтобы пользователь не увидел промежуточное состояние
    ref.current.style.top = `calc(100% - ${height}px)`;
  }, []);

  return <div ref={ref}>Текст подсказки</div>;
}

Ключевое отличие: useLayoutEffect блокирует отображение браузера, поэтому используйте его осторожно, чтобы избежать проблем с производительностью. В большинстве случаев для данных, запросов, подписок используйте useEffect.

Полный цикл на примере компонента

Рассмотрим компонент, который получает данные при монтировании и отслеживает изменения пропса.

import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  // 1. Инициализация состояния (происходит при каждом рендере)
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  // 2. Рендер (вычисление UI)
  // Здесь React вычисляет, что нужно вернуть. DOM еще не обновлен.

  // 3. После рендера и обновления DOM запускаются эффекты
  useEffect(() => {
    let isCurrent = true;
    
    setLoading(true);
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => {
        if (isCurrent) {
          setUser(data);
          setLoading(false);
        }
      });

    // Функция очистки: выполнится перед следующим вызовом этого эффекта или при размонтировании
    return () => {
      isCurrent = false;
      console.log(`Очистка эффекта для userId: ${userId}`);
    };
  }, [userId]); // Эффект зависит от userId

  // 4. Размонтирование: если компонент удаляется из дерева,
  // функция очистки в useEffect выше будет выполнена.

  if (loading) return <div>Загрузка...</div>;
  return <div>{user.name}</div>;
}

Другие важные хуки, влияющие на цикл

  • useMemo и useCallback: используются для оптимизации, предотвращая повторные вычисления или создания функций при обновлениях, если зависимости не изменились. Они не вызывают эффекты сами, но влияют на то, что передается дочерним компонентам.
  • useRef: создает объект-контейнер, который сохраняет свое значение между рендерами. Изменение ref.current не вызывает повторный рендер.
  • Контекст (useContext): позволяет компоненту реагировать на изменения контекста, что также запускает этап обновления.

Отличия от классовых компонентов

В классовых компонентах были отдельные методы (componentDidMount, componentDidUpdate, componentWillUnmount). В функциональных компонентах один хук useEffect объединяет все эти сценарии, а логика разделяется через массив зависимостей. Это делает код более линейным и позволяет группировать связанную логику (например, подписку и очистку) вместе, вместо того чтобы разбрасывать ее по разным методам класса.

Итог: Жизненный цикл функционального компонента React управляется через комбинацию хуков. useEffect — центральный инструмент для выполнения кода в ответ на монтирование, обновление и размонтирование. useLayoutEffect используется для синхронных манипуляций с DOM перед отображением. Правильное указание массива зависимостей в useEffect критически важно для корректного поведения и предотвращения бесконечных циклов или утечек памяти.