Как работает хук useEffect в React
Что проверяют
Понимаете ли, в какой момент эффект запускается и очищается, как считается массив зависимостей, и умеете ли отличить настоящий побочный эффект от кода, которому в useEffect не место.
Как отвечать
Назначение. useEffect(fn, deps) откладывает fn до момента после коммита в DOM и отрисовки кадра. Это инструмент синхронизации с внешним миром: подписки, таймеры, соединения, работа с не-React библиотекой, логирование, сетевые запросы.
Зависимости сравниваются поверхностно через Object.is:
- массива нет, эффект выполняется после каждого рендера;
- пустой массив, только после монтирования;
- со значениями, при изменении любого из них.
Объект или функция, созданные в теле компонента, каждый рендер новые, поэтому такой эффект перезапускается бесконечно. Стабилизируют через useCallback, useMemo или выносом за пределы компонента.
Очистка. Функция, возвращённая из эффекта, вызывается перед каждым следующим запуском и при размонтировании. Правило простое: всё, что эффект создал, снимается именно там. В StrictMode в разработке React намеренно монтирует, размонтирует и монтирует компонент повторно, чтобы вскрыть отсутствующую очистку. Это не баг сборки.
Запросы требуют отмены и защиты от гонки: ответ на устаревший запрос не должен перезаписать свежий.
useEffect(() => {
const controller = new AbortController();
fetch(`/api/items?q=${query}`, { signal: controller.signal })
.then((r) => r.json())
.then(setItems)
.catch((e) => { if (e.name !== "AbortError") setError(e); });
return () => controller.abort();
}, [query]);Синхронный брат. useLayoutEffect выполняется до отрисовки и нужен, когда надо измерить DOM и поправить положение без видимого мерцания. Цена в заблокированном кадре, поэтому по умолчанию берут useEffect.
Чего в эффекте быть не должно:
- вычисление производного значения, считайте его прямо при рендере или через
useMemo; - реакция на действие пользователя, это код обработчика события, а не эффекта;
- копирование пропса в состояние, лишний рендер и рассинхрон вместо одного источника правды.
Где спотыкаются
Ставят пустой массив зависимостей, чтобы «выполнилось один раз», хотя эффект читает пропсы или состояние, и получают залипшие устаревшие значения. Вторая типичная ошибка в очистке: вызывают там setState вместо того чтобы отменить запрос или снять подписку, и утечка остаётся на месте.