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

Как в функциональном компоненте можно реализовать Unmount?

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

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

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

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

Unmount в функциональных компонентах React

Что такое Unmount

Unmount - это момент, когда компонент удаляется из DOM (снимается со страницы). Это противоположно Mount (когда компонент впервые появляется на странице).

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

// Функциональный компонент:

function MyComponent() {
  // 1. RENDER (инициализация переменных, вычисления)
  console.log('Компонент рендерится');
  
  useEffect(() => {
    // 2. MOUNT (компонент добавлен в DOM)
    console.log('Компонент смонтирован');
    
    // 3. UNMOUNT (cleanup функция)
    return () => {
      console.log('Компонент удаляется');
    };
  }, []); // пустой массив = только при mount/unmount
  
  return <div>Hello</div>;
}

Реализация Unmount через useEffect

В функциональных компонентах используется return функция в useEffect:

function Timer() {
  useEffect(() => {
    console.log('Компонент смонтирован, запускаем таймер');
    
    // Запустить интервал
    const intervalId = setInterval(() => {
      console.log('Tick');
    }, 1000);
    
    // UNMOUNT: cleanup функция
    return () => {
      console.log('Компонент удаляется, очищаем интервал');
      clearInterval(intervalId);
    };
  }, []); // [] = только один раз при mount
  
  return <div>Timer running...</div>;
}

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

1. Очистка подписок (Subscriptions)

function WebSocketComponent() {
  useEffect(() => {
    // MOUNT: подключиться
    const ws = new WebSocket('wss://api.example.com');
    
    ws.onmessage = (event) => {
      console.log('Сообщение:', event.data);
    };
    
    // UNMOUNT: отключиться
    return () => {
      console.log('Отключаемся от WebSocket');
      ws.close();
    };
  }, []);
  
  return <div>WebSocket connected</div>;
}

2. Удаление Event Listeners

function ResizeHandler() {
  useEffect(() => {
    // MOUNT: добавить слушатель
    const handleResize = () => {
      console.log('Окно изменилось:', window.innerWidth);
    };
    
    window.addEventListener('resize', handleResize);
    
    // UNMOUNT: удалить слушатель
    return () => {
      console.log('Удаляем слушатель resize');
      window.removeEventListener('resize', handleResize);
    };
  }, []);
  
  return <div>Window size tracker</div>;
}

3. Отмена API запросов

function DataFetcher() {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    // MOUNT: создать AbortController для отмены запроса
    const abortController = new AbortController();
    
    fetch('/api/data', {signal: abortController.signal})
      .then(r => r.json())
      .then(data => setData(data))
      .catch(err => {
        if (err.name === 'AbortError') {
          console.log('Запрос отменён при unmount');
        }
      });
    
    // UNMOUNT: отменить запрос если компонент удалён
    return () => {
      console.log('Компонент удаляется, отменяем fetch');
      abortController.abort();
    };
  }, []);
  
  return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}

4. Очистка таймеров (setTimeout/setInterval)

function DelayedAction() {
  useEffect(() => {
    console.log('Запланировали действие на 5 секунд');
    
    const timeoutId = setTimeout(() => {
      console.log('Действие выполнено!');
    }, 5000);
    
    // UNMOUNT: отменить таймер если компонент удалён ДО срока
    return () => {
      console.log('Компонент удалён, отменяем таймер');
      clearTimeout(timeoutId);
    };
  }, []);
  
  return <div>Ожидание 5 секунд...</div>;
}

Множественные useEffect

function ComplexComponent() {
  useEffect(() => {
    console.log('Effect 1: MOUNT');
    return () => console.log('Effect 1: UNMOUNT');
  }, []);
  
  useEffect(() => {
    console.log('Effect 2: MOUNT');
    return () => console.log('Effect 2: UNMOUNT');
  }, []);
  
  return <div>Component</div>;
}

// При монтировании:
// Effect 1: MOUNT
// Effect 2: MOUNT

// При размонтировании:
// Effect 1: UNMOUNT
// Effect 2: UNMOUNT
// (в обратном порядке)

Сравнение с Class-компонентами

// CLASS КОМПОНЕНТ (старый способ)
class MyComponent extends React.Component {
  componentDidMount() {
    console.log('Компонент смонтирован');
  }
  
  componentWillUnmount() {
    console.log('Компонент удаляется');
  }
  
  render() {
    return <div>Hello</div>;
  }
}

// ФУНКЦИОНАЛЬНЫЙ КОМПОНЕНТ (современный способ)
function MyComponent() {
  useEffect(() => {
    console.log('Компонент смонтирован');
    
    return () => {
      console.log('Компонент удаляется');
    };
  }, []);
  
  return <div>Hello</div>;
}

// Результат идентичен, но функциональный компонент проще

Почему важно очищать ресурсы

// ПЛОХО - утечка памяти
function BadComponent() {
  useEffect(() => {
    const interval = setInterval(() => {
      console.log('Tick');
    }, 1000);
    // Забыли очистить interval!
    // Если компонент монтируется и размонтируется 10 раз,
    // будет 10 интервалов, работающих одновременно
  }, []);
  
  return <div>Component</div>;
}

// ХОРОШО - правильная очистка
function GoodComponent() {
  useEffect(() => {
    const interval = setInterval(() => {
      console.log('Tick');
    }, 1000);
    
    return () => clearInterval(interval); // очистили
  }, []);
  
  return <div>Component</div>;
}

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

function ChatWindow({userId}) {
  const [messages, setMessages] = useState([]);
  const [isConnected, setIsConnected] = useState(false);
  
  useEffect(() => {
    // MOUNT
    const ws = new WebSocket(`wss://chat.example.com/${userId}`);
    
    ws.onopen = () => {
      setIsConnected(true);
      console.log('WebSocket подключён');
    };
    
    ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);
      setMessages(prev => [...prev, msg]);
    };
    
    ws.onerror = (error) => {
      console.error('WebSocket ошибка:', error);
    };
    
    // UNMOUNT: очистка
    return () => {
      console.log('WebSocket закрывается');
      setIsConnected(false);
      ws.close();
    };
  }, [userId]); // пересоздать если userId изменился
  
  return (
    <div>
      <p>Статус: {isConnected ? 'Подключён' : 'Отключён'}</p>
      <ul>
        {messages.map((msg, i) => (
          <li key={i}>{msg.text}</li>
        ))}
      </ul>
    </div>
  );
}

Вывод

Unmount в функциональных компонентах реализуется через return функцию в useEffect. Это критично для:

  • Очистки таймеров и интервалов
  • Отписки от событий
  • Закрытия WebSocket подключений
  • Отмены API запросов
  • Предотвращения утечек памяти

Это один из самых важных паттернов в React!