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

Какой хук имитирует методы жизненного цикла?

2.0 Middle🔥 122 комментариев
#JavaScript Core

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

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

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

useLayoutEffect и useEffect: хуки, имитирующие методы жизненного цикла React

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

Основные соответствия между хуками и методами жизненного цикла

1. componentDidMount + componentDidUpdate = useEffect

Хук useEffect с зависимостями или без них наиболее точно имитирует комбинацию этих двух методов:

import React, { useEffect } from 'react';

function MyComponent({ userId }) {
  // Аналог componentDidMount (пустой массив зависимостей)
  useEffect(() => {
    console.log('Компонент смонтирован');
    // Подписки на события, запросы к API
    return () => {
      console.log('Компонент будет размонтирован');
      // Аналог componentWillUnmount - функция очистки
    };
  }, []);

  // Аналог componentDidUpdate для конкретной пропсы
  useEffect(() => {
    if (userId) {
      console.log(`userId изменился: ${userId}`);
      fetchUserData(userId);
    }
  }, [userId]); // Зависимость от userId

  return <div>Компонент</div>;
}

2. componentWillUnmount = функция очистки в useEffect

Функция, возвращаемая из useEffect, выполняется при размонтировании компонента:

useEffect(() => {
  const timer = setInterval(() => {
    console.log('Таймер тикает');
  }, 1000);

  // Функция очистки - аналог componentWillUnmount
  return () => {
    clearInterval(timer);
    console.log('Таймер очищен');
  };
}, []);

3. getDerivedStateFromProps = useState + useEffect

Для реализации логики, аналогичной getDerivedStateFromProps, используется комбинация хуков:

function MyComponent({ initialValue }) {
  const [value, setValue] = useState(initialValue);

  // Синхронизация при изменении пропсов
  useEffect(() => {
    setValue(initialValue);
  }, [initialValue]);

  // Или более оптимально - во время рендера
  const [prevInitialValue, setPrevInitialValue] = useState(initialValue);
  if (initialValue !== prevInitialValue) {
    setValue(initialValue);
    setPrevInitialValue(initialValue);
  }
}

4. shouldComponentUpdate = React.memo + useMemo/useCallback

Для оптимизации рендеринга используются:

import React, { memo, useMemo } from 'react';

// React.memo для поверхностного сравнения пропсов
const MyComponent = memo(function MyComponent({ data, onClick }) {
  // useMemo для мемоизации вычислений
  const processedData = useMemo(() => {
    return data.map(item => item * 2);
  }, [data]); // Пересчитывается только при изменении data

  return <div>{processedData}</div>;
});

// Кастомное сравнение (аналог shouldComponentUpdate)
const MyComponentWithCustomCompare = memo(
  MyComponent,
  (prevProps, nextProps) => {
    // Возвращаем true, если пропсы равны (рендер не нужен)
    return prevProps.id === nextProps.id;
  }
);

useLayoutEffect: синхронный аналог componentDidMount/DidUpdate

Хук useLayoutEffect выполняется синхронно после рендера, но до отрисовки в браузере. Это делает его идеальным для:

import React, { useLayoutEffect, useRef } from 'react';

function Tooltip({ text }) {
  const tooltipRef = useRef();

  useLayoutEffect(() => {
    const { width, height } = tooltipRef.current.getBoundingClientRect();
    // Измерения DOM и синхронные обновления
    if (width > 300) {
      tooltipRef.current.style.fontSize = '12px';
    }
  }, [text]); // Зависимость от text

  return <div ref={tooltipRef}>{text}</div>;
}

Ключевые различия между useEffect и useLayoutEffect

  • useEffect:

    • Выполняется асинхронно после рендера и отрисовки
    • Не блокирует отрисовку браузера
    • Используется для сайд-эффектов: запросы к API, подписки
  • useLayoutEffect:

    • Выполняется синхронно после рендера, но до отрисовки
    • Может блокировать отрисовку при тяжелых операциях
    • Используется для измерений DOM и синхронных обновлений

Практические рекомендации

  1. Начинайте с useEffect - это хук по умолчанию для большинства случаев
  2. Используйте useLayoutEffect только когда нужно синхронное обновление DOM
  3. Оптимизируйте зависимости - правильно указывайте массив зависимостей
  4. Разделяйте логику - несколько useEffect для разных ответственностей
  5. Не забывайте об очистке - всегда возвращайте функцию очистки при подписках

Пример полного жизненного цикла на хуках

function ComponentWithLifecycle({ id, data }) {
  // Инициализация состояния (аналог constructor)
  const [state, setState] = useState(() => {
    // Ленивая инициализация состояния
    return computeInitialState();
  });

  // getDerivedStateFromProps
  const [prevId, setPrevId] = useState(id);
  if (id !== prevId) {
    setState(computeStateFromProps(id));
    setPrevId(id);
  }

  // componentDidMount + componentDidUpdate
  useEffect(() => {
    fetchData(id);
    
    // componentWillUnmount
    return () => {
      cleanup();
    };
  }, [id]);

  // shouldComponentUpdate (оптимизация)
  const memoizedValue = useMemo(() => computeExpensiveValue(data), [data]);

  return <div>{memoizedValue}</div>;
}

Таким образом, React хуки предоставляют более композируемый и предсказуемый способ управления жизненным циклом компонентов по сравнению с классическими методами, позволяя лучше организовывать логику и избегать распространенных ошибок, связанных с жизненным циклом классовых компонентов.

Какой хук имитирует методы жизненного цикла? | PrepBro