[Medium] React useEffect и Virtual DOM
1. Что такое useEffect?
Что делает
useEffectв React?
useEffect выполняет побочные эффекты после того, как React зафиксирует обновления UI.
Типичные побочные эффекты:
- Получение удалённых данных
- Подписка на события браузера
- Синхронизация с API, такими как
document.titleилиlocalStorage - Запуск и очистка таймеров
import { useEffect, useState } from 'react';
export default function CounterTitle() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Счётчик: ${count}`;
}, [count]);
return (
<button type="button" onClick={() => setCount((v) => v + 1)}>
Клик {count}
</button>
);
}
2. Как работает массив зависимостей?
Без массива зависимостей
Выполняется после каждого рендера.
useEffect(() => {
console.log('выполняется после каждого рендера');
});
Пустой массив зависимостей []
Выполняется один раз при монтировании и очистка при размонтировании.
useEffect(() => {
console.log('только при монтировании');
return () => console.log('очистка при размонтировании');
}, []);
Конкретные зависимости
Выполняется при монтировании и при изменении любого из указанных значений.
useEffect(() => {
console.log('query изменился:', query);
}, [query]);
3. Почему важна очистка
Верните функцию из useEffect для освобождения ресурсов.
useEffect(() => {
const onResize = () => console.log(window.innerWidth);
window.addEventListener('resize', onResize);
return () => {
window.removeEventListener('resize', onResize);
};
}, []);
Без очистки возможны утечки слушателей, таймеров или устаревших асинхронных операций.
4. Распространённые ошибки useEffect
1) Бесконечные перерисовки
// плохо: обновляет состояние при каждом рендере
useEffect(() => {
setCount((v) => v + 1);
});
Исправление: добавить правильный массив зависимостей или перенести логику в другое место.
2) Пропущенные зависимости
Если вы используете переменную внутри эффекта, включите её в зависимости, если она не является намеренно стабильной.
3) Устаревшие замыкания
Эффекты захватывают значения из того рендера, в котором были созданы. Используйте refs или обновление зависимостей при необходимости.
4) Производные вычисления в эффекте
Если значение можно вычислить напрямую из props/state, предпочтительнее useMemo или простое вычисление вместо useEffect.
5. Что такое Virtual DOM?
Virtual DOM — это представление UI в памяти. React сравнивает предыдущее и следующее виртуальные деревья, затем обновляет только необходимые части реального DOM.
Почему это помогает
- Уменьшает ручные обновления DOM
- Обеспечивает предсказуемые обновления UI на основе состояния
- Использует эвристики согласования для эффективности
Это не всегда «бесплатно»; создание и сравнение деревьев тоже имеет стоимость.
6. Согласование (Reconciliation) простыми словами
При изменении состояния:
- React строит новое виртуальное дерево
- React сравнивает его с предыдущим деревом
- React фиксирует минимальные изменения в реальном DOM
Ключи в списках критически важны, потому что помогают React правильно сопоставить старые и новые узлы.
{items.map((item) => (
<li key={item.id}>{item.label}</li>
))}
Стабильные ключи предотвращают некорректное переиспользование и ненужные перемонтирования.
7. Взаимосвязь useEffect и Virtual DOM
- Virtual DOM diffing происходит во время фазы рендеринга/согласования
useEffectвыполняется после фазы фиксации- Поэтому
useEffectв большинстве случаев не блокирует отрисовку
Если необходимо синхронно прочитать/записать layout перед отрисовкой, осторожно используйте useLayoutEffect.
8. Практические паттерны
Загрузка данных с защитой от отмены
useEffect(() => {
let cancelled = false;
async function load() {
const res = await fetch('/api/user');
const data = await res.json();
if (!cancelled) setUser(data);
}
load();
return () => {
cancelled = true;
};
}, []);
Вынос переиспользуемой логики эффектов в пользовательские хуки
function useDocumentTitle(title: string) {
useEffect(() => {
document.title = title;
}, [title]);
}
9. Быстрые ответы для собеседования
В1: Является ли useEffect эквивалентом методов жизненного цикла классов?
Концептуально да, для побочных эффектов, но ментальная модель — «после рендера» с повторными запусками на основе зависимостей.
В2: Означает ли Virtual DOM, что обновления React всегда быстрые?
Не всегда. Производительность по-прежнему зависит от структуры компонентов, частоты рендеринга, мемоизации и стабильности ключей.
В3: Когда следует избегать useEffect?
Избегайте его для чистых производных значений и обработчиков событий, не требующих внешней синхронизации.