Хотите заказать веб-сайт? Связаться с нами

Жизненный цикл React: mount, update, unmount

Цель урока: понять, что происходит с React-компонентом на каждом этапе его существования: от создания до удаления. Научиться выполнять код в нужные моменты — при загрузке данных, при обновлении или перед тем, как компонент исчезает с экрана.

Жизненный цикл React: mount, update, unmount

Введение: что такое жизненный цикл

Каждый компонент в React проходит через три основные фазы:

Фаза Что происходит Что можно делать
Монтирование (Mounting) Компонент появляется на экране Загрузить данные, подписаться на события
Обновление (Updating) Компонент перерисовывается (изменился state или props) Реагировать на изменения, обновлять DOM
Размонтирование (Unmounting) Компонент исчезает с экрана Отписаться от событий, очистить таймеры

Главная мысль: у каждого компонента есть «жизненный цикл» — моменты, когда он рождается, обновляется и умирает. На этих этапах можно выполнять нужные действия.

Жизненный цикл в функциональных компонентах (хуки)

В современных React-приложениях (с хуками) жизненным циклом управляет хук useEffect.

Важно: в функциональных компонентах нет отдельных методов, как в классовых. Всё делается через useEffect с разными зависимостями.

Сопоставление классовых методов и хуков

Классовый компонент Функциональный компонент (хуки)
componentDidMount useEffect(() => {...}, [])
componentDidUpdate useEffect(() => {...}, [deps])
componentWillUnmount useEffect(() => { return () => {...} }, [])

Хук useEffect — главный инструмент

Синтаксис:

useEffect(() => {
  // Код, который выполняется при монтировании и обновлении

  return () => {
    // Очистка (выполняется при размонтировании)
  };
}, [зависимости]);

Три варианта использования:

Вариант Код Когда выполняется
Без зависимостей useEffect(() => {...}) При каждом рендере
Пустой массив useEffect(() => {...}, []) Один раз при монтировании
С зависимостями useEffect(() => {...}, [count]) При монтировании и при изменении count

Фаза 1. Монтирование (Mounting)

Что происходит:

  1. React создаёт DOM-элементы
  2. Вставляет их на страницу
  3. Выполняется код после рендера

Как это выглядит в коде:

import { useEffect } from 'react';

function UserProfile({ userId }) {
  useEffect(() => {
    // Код выполняется один раз после первого рендера
    console.log('Компонент смонтирован на страницу');
    
    // Загружаем данные пользователя
    fetchUserData(userId);
    
    // Подписываемся на события
    const handleResize = () => console.log('Окно изменено');
    window.addEventListener('resize', handleResize);
    
    // Возвращаем функцию очистки (будет вызвана при размонтировании)
    return () => {
      console.log('Компонент будет удалён');
      window.removeEventListener('resize', handleResize);
    };
  }, []); // Пустой массив = только при монтировании

  return <div>Профиль пользователя</div>;
}

Типичные задачи при монтировании:

  1. Загрузка данных с сервера (API-запросы)
  2. Подписка на события (resize, scroll, WebSocket)
  3. Настройка таймеров (setInterval, setTimeout)
  4. Работа с DOM (фокус, размеры)
Из ноутбука появляются летающие светящиеся экраны

Оживи свой сайт. Освой JavaScript!

Статичная верстка — это только скелет. Наш онлайн-курс "JavaScript с нуля до профи" даст твоим страницам мышцы и нервы. Научись создавать слайдеры, формы, интерактивные карты и получать данные с сервера.

От теории — к реальным скриптам в твоём портфолио.

Подробнее о курсе

Фаза 2. Обновление (Updating)

Что происходит:

  1. Меняется state или props
  2. React перерисовывает компонент
  3. Выполняется эффект (если зависимости изменились)

Как это выглядит в коде:

function Counter() {
  const [count, setCount] = useState(0);
  const [doubledCount, setDoubledCount] = useState(0);

  useEffect(() => {
    // Код выполняется при каждом изменении count
    console.log(`Счёт изменился: ${count}`);
    setDoubledCount(count * 2);
  }, [count]); // Выполняется только когда count меняется

  return (
    <div>
      <p>Счёт: {count}</p>
      <p>Удвоенный счёт: {doubledCount}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

Типичные задачи при обновлении:

  1. Реагировать на изменение props
  2. Пересчитывать производные данные
  3. Обновлять DOM в зависимости от изменений
  4. Отправлять аналитику

Фаза 3. Размонтирование (Unmounting)

Что происходит:

  1. Компонент удаляется из DOM
  2. Выполняется функция очистки

Как это выглядит в коде:

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    // Настраиваем таймер при монтировании
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    // Очищаем таймер при размонтировании
    return () => {
      clearInterval(interval);
      console.log('Таймер остановлен');
    };
  }, []);

  return <p>Прошло секунд: {seconds}</p>;
}

Типичные задачи при размонтировании:

  1. Остановка таймеров (setInterval, setTimeout)
  2. Отписка от событий (removeEventListener)
  3. Закрытие соединений (WebSocket)
  4. Отмена запросов (AbortController)
  5. Очистка подписок

useEffect с зависимостями — подробно

Пустой массив [] — один раз при монтировании:

useEffect(() => {
  // Выполняется один раз
  console.log('Только при монтировании');
}, []);

Массив с переменными — при их изменении:

useEffect(() => {
  console.log(`Изменился пропс userId: ${userId}`);
}, [userId]);

Несколько зависимостей:

useEffect(() => {
  console.log(`userId или userRole изменились`);
}, [userId, userRole]);

Без массива — при каждом рендере (используй осторожно!):

useEffect(() => {
  console.log('Выполняется при каждом рендере');
}); // Нет массива зависимостей

Частые сценарии использования useEffect

1. Загрузка данных при монтировании:

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchUsers = async () => {
      const response = await fetch('/api/users');
      const data = await response.json();
      setUsers(data);
      setLoading(false);
    };
    
    fetchUsers();
  }, []); // Загружаем только один раз

  if (loading) return <p>Загрузка...</p>;
  return <div>{users.map(user => <p key={user.id}>{user.name}</p>)}</div>;
}

2. Подписка на событие с очисткой:

function WindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    
    window.addEventListener('resize', handleResize);
    
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return <p>Ширина окна: {width}px</p>;
}

3. Автосохранение при изменении данных:

function NoteEditor() {
  const [content, setContent] = useState('');

  useEffect(() => {
    // Автосохраняем каждые 3 секунды при изменении content
    const timer = setTimeout(() => {
      localStorage.setItem('note', content);
      console.log('Заметка сохранена');
    }, 3000);

    return () => clearTimeout(timer);
  }, [content]);

  return <textarea value={content} onChange={(e) => setContent(e.target.value)} />;
}

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

Для понимания legacy-кода:

class UserProfile extends React.Component {
  // 1. Монтирование
  componentDidMount() {
    console.log('Компонент смонтирован');
    this.loadData();
  }

  // 2. Обновление
  componentDidUpdate(prevProps, prevState) {
    if (this.props.userId !== prevProps.userId) {
      console.log('userId изменился');
      this.loadData();
    }
  }

  // 3. Размонтирование
  componentWillUnmount() {
    console.log('Компонент будет удалён');
    clearInterval(this.interval);
  }

  loadData() {
    // Загрузка данных
  }

  render() {
    return <div>Профиль</div>;
  }
}

useEffect и асинхронные операции

Важно: useEffect не может быть асинхронным напрямую:

❌ Неправильно:

useEffect(async () => {
  const data = await fetchData(); // Ошибка!
}, []);

✅ Правильно (создаём функцию внутри):

useEffect(() => {
  const fetchData = async () => {
    const response = await fetch('/api/data');
    const data = await response.json();
    setData(data);
  };
  
  fetchData();
}, []);

✅ Или с IIFE (немедленно вызываемая функция):

useEffect(() => {
  (async () => {
    const data = await fetchData();
    setData(data);
  })();
}, []);

Частые ошибки новичков

Ошибка Почему Исправление
Забывают про очистку Таймеры/подписки продолжают работать Всегда возвращай функцию очистки
Пустой массив при использовании props Не реагирует на изменения Добавь props в зависимости
Бесконечный цикл Обновляешь state без зависимостей Добавь зависимости или проверь логику
Асинхронный useEffect напрямую async не поддерживается Создай функцию внутри
Используют useEffect для всего Избыточное количество эффектов Используй только когда нужно

Итог (что мы узнали)

Что узнали Как это работает
Жизненный цикл Монтирование → Обновление → Размонтирование
useEffect Главный хук для управления жизненным циклом
Пустой массив [] Выполнить один раз при монтировании
Зависимости Выполнять при изменении переменных
Функция очистки Выполняется при размонтировании
Асинхронные операции Создавать функцию внутри useEffect

Задание для закрепления (на 10–12 минут)

Создай компонент UserDataFetcher, который:

  1. При монтировании загружает данные пользователя по userId (используй setTimeout для имитации запроса на 1 секунду)
  2. Показывает текст «Загрузка…» во время загрузки
  3. После загрузки показывает: имя, email, возраст
  4. При изменении userId (передаётся через props) — перезагружает данные
  5. При размонтировании компонента отменяет запрос (используй AbortController или флаг)

Дополнительно: Добавь кнопку «Обновить», которая перезагружает данные с показом загрузки.

Проверка: Меняй userId в родительском компоненте и убедись, что данные обновляются. Добавь кнопку «Удалить компонент», чтобы проверить размонтирование.

Что дальше?

В следующем уроке разберём Lists and Keys — как работать с массивами данных в React. Ты научишься рендерить списки, добавлять и удалять элементы, правильно использовать ключи (key) и оптимизировать отображение больших данных. Это один из самых частых сценариев в реальных приложениях.

Теги: