← Назад к вопросам
Зачем нужно размонтирование в React?
2.3 Middle🔥 271 комментариев
#React#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Зачем нужно размонтирование в React?
Размонтирование (unmounting) в React — это процесс удаления компонента из DOM. Это критически важно для корректной очистки ресурсов, предотвращения утечек памяти и избежания побочных эффектов, которые продолжают работать после удаления компонента. React предоставляет хуки и методы для контролирования процесса размонтирования.
Жизненный цикл компонента
Размонтирование — это последняя фаза жизненного цикла:
Монтирование → Обновление → Размонтирование
(render) (re-render) (cleanup)
function Component() {
// 1. МОНТИРОВАНИЕ (первый рендер)
console.log('Component mounted');
// 2. ОБНОВЛЕНИЕ (если пропсы или состояние изменились)
// console.log('Component updated');
// 3. РАЗМОНТИРОВАНИЕ (удаление из DOM)
// cleanup функция сработает
return <div>Hello</div>;
}
Очистка ресурсов в useEffect
Основной способ управления размонтированием — это функция очистки в useEffect:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Монтирование: загрузить данные пользователя
let isMounted = true; // Флаг для отслеживания жизненного цикла
async function fetchUser() {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
// Установить состояние только если компонент ещё смонтирован
if (isMounted) {
setUser(data);
setLoading(false);
}
}
fetchUser();
// Размонтирование: очистка
return () => {
isMounted = false; // Отметить, что компонент размонтирован
// Здесь выполнится очистка
};
}, [userId]);
if (loading) return <div>Loading...</div>;
return <div>{user?.name}</div>;
}
Отписка от подписок
function RealtimeChat() {
const [messages, setMessages] = useState([]);
useEffect(() => {
// Монтирование: подписаться на обновления
const unsubscribe = subscribeToChat((newMessage) => {
setMessages(prev => [...prev, newMessage]);
});
// Размонтирование: отписаться
return () => {
unsubscribe(); // Прекратить слушать сообщения
};
}, []);
return (
<div>
{messages.map(msg => <div key={msg.id}>{msg.text}</div>)}
</div>
);
}
Очистка таймеров и интервалов
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
// Монтирование: создать интервал
const intervalId = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
// Размонтирование: очистить интервал
return () => {
clearInterval(intervalId); // Предотвратить утечку памяти
};
}, []);
return <div>{seconds}s</div>;
}
// ВАЖНО: без очистки интервал продолжает работать в памяти даже
// после размонтирования компонента, вызывая утечку памяти
Очистка слушателей событий
function WindowResizeHandler() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
// Монтирование: добавить слушатель
const handleResize = () => {
setWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
// Размонтирование: удалить слушатель
return () => {
window.removeEventListener('resize', handleResize);
// Без этого слушатель остаётся в памяти
};
}, []);
return <div>Width: {width}px</div>;
}
Отмена запросов
function SearchResults({ query }) {
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
// Создать AbortController для отмены запроса
const controller = new AbortController();
// Монтирование: отправить запрос
fetch(`/api/search?q=${query}`, {
signal: controller.signal
})
.then(res => res.json())
.then(data => {
// Результаты придут после размонтирования
// попытаются обновить состояние несуществующего компонента
setResults(data);
setLoading(false);
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('Request was cancelled');
}
});
// Размонтирование: отменить запрос
return () => {
controller.abort(); // Отменить fetch запрос
};
}, [query]);
return (
<div>
{loading && <div>Searching...</div>}
{results.map(item => <div key={item.id}>{item.title}</div>)}
</div>
);
}
Проблема: Warning о setState после размонтирования
// ❌ ПЛОХО - вызывает warning
function BadComponent() {
const [data, setData] = useState(null);
useEffect(() => {
setTimeout(() => {
setData('loaded'); // Может срабатить после размонтирования!
}, 3000);
}, []);
return <div>{data}</div>;
}
// ✅ ХОРОШО - с очисткой
function GoodComponent() {
const [data, setData] = useState(null);
useEffect(() => {
let timeoutId;
let isMounted = true;
timeoutId = setTimeout(() => {
if (isMounted) { // Проверка перед setState
setData('loaded');
}
}, 3000);
return () => {
isMounted = false;
clearTimeout(timeoutId);
};
}, []);
return <div>{data}</div>;
}
Очистка в классовых компонентах
class UserProfile extends React.Component {
constructor(props) {
super(props);
this.state = { user: null };
}
componentDidMount() {
// Монтирование
this.subscription = subscribeToUser(this.props.userId, (user) => {
this.setState({ user });
});
}
componentWillUnmount() {
// Размонтирование: очистка
if (this.subscription) {
this.subscription.unsubscribe();
}
}
render() {
return <div>{this.state.user?.name}</div>;
}
}
Практический пример: модальное окно
function Modal({ isOpen, onClose, children }) {
useEffect(() => {
if (!isOpen) return;
// Монтирование: блокировать скролл
const originalOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
// Монтирование: слушатель на Escape
const handleKeyDown = (e) => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('keydown', handleKeyDown);
// Размонтирование: восстановить всё
return () => {
document.body.style.overflow = originalOverflow;
document.removeEventListener('keydown', handleKeyDown);
};
}, [isOpen, onClose]);
if (!isOpen) return null;
return (
<div className="modal">
<button onClick={onClose}>Close</button>
{children}
</div>
);
}
Чеклист очистки ресурсов
При размонтировании убедись, что очищены:
- ✅ Таймеры (
clearTimeout,clearInterval) - ✅ Слушатели событий (
removeEventListener) - ✅ Подписки (
unsubscribe) - ✅ Сетевые запросы (
AbortController) - ✅ Web Socket соединения
- ✅ Глобальные переменные
- ✅ DOM изменения (стили, классы на document.body)
Итоги
- Размонтирование — удаление компонента и очистка его ресурсов
- Критично для предотвращения утечек памяти и побочных эффектов
- useEffect cleanup function (
return () => {...}) срабатывает при размонтировании - Обязательная очистка: таймеры, события, подписки, сетевые запросы
- Warning о setState после размонтирования указывает на пропущенную очистку
- Это отличает хороший код от плохого в production приложениях