А если совсем не передавать массив зависимостей
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что происходит если не передавать массив зависимостей в useEffect
Это критичный вопрос, который часто задают на интервью. Поведение отличается от случаев, когда передаёшь пустой массив [] или конкретные зависимости.
Три варианта
1. useEffect БЕЗ массива зависимостей
useEffect(() => {
console.log('Эффект выполнен');
// НЕТ второго параметра
});
Поведение:
- Эффект выполняется ПОСЛЕ КАЖДОГО рендера компонента
- Выполняется каждый раз, когда компонент перерисовывается
- Идеальна для эффектов, которые должны синхронизироваться с каждым обновлением
2. useEffect с пустым массивом []
useEffect(() => {
console.log('Эффект выполнен');
}, []);
Поведение:
- Эффект выполняется ОДИН РАЗ при монтировании компонента
- НЕ выполняется при обновлениях
- Используется для инициализации и загрузки данных
3. useEffect с конкретными зависимостями
useEffect(() => {
console.log('Эффект выполнен');
}, [dependency]);
Поведение:
- Эффект выполняется при ИЗМЕНЕНИИ зависимостей
- Выполняется один раз при монтировании
- Затем выполняется когда
dependencyизменяется
Практический пример
function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// БЕЗ массива зависимостей
useEffect(() => {
console.log('Без зависимостей: выполняется после КАЖДОГО рендера');
});
// С пустым массивом
useEffect(() => {
console.log('С []: выполняется ОДИН РАЗ при монтировании');
}, []);
// С зависимостями
useEffect(() => {
console.log('С [count]: выполняется при изменении count');
}, [count]);
return (
<div>
<p>Счётчик: {count}</p>
<p>Имя: {name}</p>
<button onClick={() => setCount(count + 1)}>Увеличить счётчик</button>
<button onClick={() => setName('Иван')}>Установить имя</button>
</div>
);
}
Порядок выполнения
Когда ты нажимаешь кнопку "Увеличить счётчик":
1. State обновляется (count = 1)
2. Компонент перерисовывается
3. Эффект БЕЗ зависимостей выполняется
4. Эффект с [count] выполняется
5. Эффект с [] НЕ выполняется
Когда ты нажимаешь кнопку "Установить имя":
1. State обновляется (name = 'Иван')
2. Компонент перерисовывается
3. Эффект БЕЗ зависимостей выполняется
4. Эффект с [count] НЕ выполняется
5. Эффект с [] НЕ выполняется
Когда это может быть проблемой
Бесконечный цикл
function BadComponent() {
const [data, setData] = useState(null);
useEffect(() => {
// БЕЗ массива зависимостей - ПЛОХО!
fetch('/api/data')
.then(res => res.json())
.then(result => setData(result)); // Обновляет state
// Это вызывает новый рендер
// Который снова запускает эффект
// Бесконечный цикл!
});
return <div>{data?.name}</div>;
}
Результат: бесконечные запросы к серверу, падение приложения.
Правильный вариант
function GoodComponent() {
const [data, setData] = useState(null);
useEffect(() => {
// С пустым массивом - ПРАВИЛЬНО
fetch('/api/data')
.then(res => res.json())
.then(result => setData(result));
}, []); // Выполнится только один раз
return <div>{data?.name}</div>;
}
Когда нужно БЕЗ массива зависимостей
Используй useEffect без массива зависимостей редко, но есть легальные случаи:
Пример 1: Синхронизация title
function Page({ title }) {
useEffect(() => {
document.title = title; // Обновляется после каждого рендера
// Это нормально и необходимо
});
return <div>Содержание</div>;
}
Пример 2: Отслеживание размера окна
function ResponsiveComponent() {
const [windowSize, setWindowSize] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => {
setWindowSize(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}); // БЕЗ зависимостей - нормально для этого случая
return <div>Ширина: {windowSize}</div>;
}
Сравнительная таблица
| Вариант | Количество выполнений | Когда | Случаи использования |
|---|---|---|---|
| Без массива | После КАЖДОГО рендера | Всегда | Редко, синхронизация |
[] | 1 (при монтировании) | Один раз | Загрузка данных, инициализация |
[dep1, dep2] | Когда изменяются зависимости | При изменении | Реактивные обновления |
ESLint правило
React ESLint plugin предупреждает об отсутствии массива зависимостей:
// ESLint предупредит
useEffect(() => {
console.log(count); // count используется, но не в зависимостях
}, []); // ОШИБКА: count должен быть в зависимостях
// Правильно
useEffect(() => {
console.log(count);
}, [count]); // ОК
Чтобы игнорировать, можно добавить:
useEffect(() => {
// ...
}, []); // eslint-disable-next-line react-hooks/exhaustive-deps
НО это плохая практика без серьёзной причины.
Лучшие практики
-
Всегда указывай массив зависимостей
- Либо пустой
[]для инициализации - Либо с конкретными зависимостями
- Либо пустой
-
Изучи ESLint правила для React Hooks
{ "extends": ["plugin:react-hooks/recommended"] } -
Если забыл зависимость, ESLint подскажет:
"count" is not included in the dependency array -
Тестируй поведение в devtools
useEffect(() => { console.log('Эффект выполнен'); }, []); // Поставь точку останова и смотри когда выполняется
Часто задаваемый вопрос
Вопрос: Почему ESLint требует указывать все переменные в зависимостях?
Ответ: Потому что React нужно знать, когда пересчитывать эффект. Если ты используешь переменную, но не указал её в зависимостях, эффект может использовать старые значения (stale closure).
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
console.log(count); // Какой count будет тут?
}, 3000);
}, []); // ОШИБКА: count должен быть в зависимостях
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Увеличить</button>
</div>
);
}
// Если нажать кнопку несколько раз и подождать 3 сек
// Выведет 0 (старое значение), а не текущее
Заключение
Не передавать массив зависимостей в useEffect — это обычно ОШИБКА. Эффект будет выполняться после каждого рендера, что часто приводит к бесконечным циклам и проблемам производительности. Всегда указывай массив зависимостей или пустой [] для контроля когда выполняется эффект.