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

К каким жизненным циклам компонента дает доступ UseEffect

1.0 Junior🔥 221 комментариев
#React

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

useEffect и жизненные циклы React компонента

useEffect дает доступ ко всем основным фазам жизненного цикла компонента: монтирование (mount), обновление (update), демонтирование (unmount). Это главная причина, почему useEffect считается комбинацией componentDidMount, componentDidUpdate и componentWillUnmount.

Три фазы жизненного цикла

1. Монтирование (Mount) — компонент создаётся

useEffect(() => {
  console.log('Компонент СМОНТИРОВАН');
  // Этот код выполнится один раз после первого рендера
}, []); // Пустой массив зависимостей = только при монтировании

2. Обновление (Update) — props или state изменились

useEffect(() => {
  console.log('userId изменился или компонент ререндерился');
  // Этот код выполнится после КАЖДОГО рендера
}, [userId]); // Зависит от userId

3. Демонтирование (Unmount) — компонент удаляется из DOM

useEffect(() => {
  return () => {
    console.log('Компонент будет РАЗМОНТИРОВАН');
    // Cleanup код
  };
}, []);

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

import { useEffect, useState } from 'react';

export function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  // 1. МОНТИРОВАНИЕ
  useEffect(() => {
    console.log('1. Компонент смонтирован, userId:', userId);
    return () => {
      console.log('3. Компонент размонтирован');
    };
  }, []); // Только при первом монтировании

  // 2. ОБНОВЛЕНИЕ (когда userId изменился)
  useEffect(() => {
    console.log('2. userId изменился на', userId);
    
    setLoading(true);
    fetch(`/api/users/${userId}`)
      .then(r => r.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      });

    // Cleanup перед переходом на нового пользователя
    return () => {
      console.log('Cleanup: отмена загрузки для userId', userId);
      // Отмена fetch запроса (AbortController)
    };
  }, [userId]); // Зависит от userId

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

Варианты с разными зависимостями

1. Без массива зависимостей — выполняется после КАЖДОГО рендера

useEffect(() => {
  console.log('Этот код выполнится после КАЖДОГО рендера');
});
// Эквивалент: componentDidMount + componentDidUpdate

2. Пустой массив [] — только при монтировании и размонтировании

useEffect(() => {
  console.log('Монтирование');
  return () => {
    console.log('Размонтирование');
  };
}, []);
// Эквивалент: componentDidMount + componentWillUnmount

3. С зависимостями [dep1, dep2] — когда зависимости изменились

useEffect(() => {
  console.log('Очистка старого эффекта');
  // Код эффекта
  return () => {
    console.log('Cleanup перед новым эффектом');
  };
}, [dep1, dep2]);
// Эквивалент: componentDidUpdate (когда dep1 или dep2 изменились)

Практические примеры

Пример 1: Подписка на событие (типичный паттерн)

function ResizeListener() {
  // МОНТИРОВАНИЕ: подписываемся
  // ОБНОВЛЕНИЕ: ничего (нет зависимостей)
  // РАЗМОНТИРОВАНИЕ: отписываемся
  useEffect(() => {
    const handleResize = () => {
      console.log('Окно ресайзилось');
    };

    window.addEventListener('resize', handleResize);

    // Cleanup: отписываемся
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []); // Только при монтировании/размонтировании

  return <div>Слушаю resize</div>;
}

Пример 2: Таймер

function Timer({ seconds }) {
  // МОНТИРОВАНИЕ: запускаем таймер
  // ОБНОВЛЕНИЕ: отмена старого, запуск нового (если seconds изменился)
  // РАЗМОНТИРОВАНИЕ: отмена таймера
  useEffect(() => {
    const interval = setInterval(() => {
      console.log('Тик');
    }, seconds * 1000);

    return () => {
      clearInterval(interval); // Cleanup
    };
  }, [seconds]);

  return <div>Таймер работает</div>;
}

Пример 3: Загрузка данных

function Posts({ page }) {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // МОНТИРОВАНИЕ: загружаем posts для page 1
    // ОБНОВЛЕНИЕ: загружаем posts для нового page
    // РАЗМОНТИРОВАНИЕ: отмена запроса
    
    const controller = new AbortController();
    
    setLoading(true);
    fetch(`/api/posts?page=${page}`, { signal: controller.signal })
      .then(r => r.json())
      .then(data => {
        setPosts(data);
        setLoading(false);
      })
      .catch(() => {
        // Может ошибка от отмены (AbortError) — игнорируем
      });

    // Cleanup: отмена запроса
    return () => {
      controller.abort();
    };
  }, [page]); // Перезапускаем когда page изменился

  return <div>{loading ? 'Загрузка' : posts.length}</div>;
}

Сравнение с классовыми компонентами

// Класс
class UserProfile extends React.Component {
  componentDidMount() {
    // МОНТИРОВАНИЕ
    console.log('Смонтирован');
  }

  componentDidUpdate(prevProps) {
    // ОБНОВЛЕНИЕ
    if (prevProps.userId !== this.props.userId) {
      console.log('userId изменился');
    }
  }

  componentWillUnmount() {
    // РАЗМОНТИРОВАНИЕ
    console.log('Размонтирован');
  }
}

// Функциональный компонент с useEffect
function UserProfile({ userId }) {
  // МОНТИРОВАНИЕ + РАЗМОНТИРОВАНИЕ
  useEffect(() => {
    console.log('Смонтирован');
    return () => console.log('Размонтирован');
  }, []);

  // ОБНОВЛЕНИЕ (когда userId изменился)
  useEffect(() => {
    console.log('userId изменился');
  }, [userId]);
}

Важные моменты

  1. Порядок выполнения:

    • Рендер компонента
    • Браузер рисует (paint)
    • Выполняются эффекты (useEffect)
    • Cleanup старых эффектов
  2. Не блокирует рендер:

    • useEffect асинхронно, не блокирует UI
    • Для синхронных операций используй useLayoutEffect
  3. Очистка (cleanup) важна:

    • Отписывайся от событий
    • Отменяй запросы
    • Очищай таймеры
    • Закрывай соединения

Итог

useEffect дает полный доступ к жизненному циклу:

  • Монтирование → useEffect с пустыми зависимостями
  • Обновление → useEffect с нужными зависимостями
  • Размонтирование → return функция в useEffect

Это мощный инструмент, который заменяет все три метода класса в одном хуке.