Какие уязвимости или баги известны у React хука useEffect и как получить неожиданное поведение
Что проверяют
Понимаете ли вы, как эффект связан с рендером и замыканием, а не просто помните про массив зависимостей. Слово «уязвимости» тут про ошибки поведения: дыр в безопасности у самого useEffect нет. Следом обычно просят найти баг в куске кода.
Как отвечать
Начните с модели: эффект запускается после отрисовки, видит значения того рендера, в котором создан, и перезапускается, если хоть одна зависимость изменилась по Object.is. Перед перезапуском и при размонтировании вызывается cleanup. Отсюда типовые баги:
- устаревшее замыкание: зависимость не указана, и код внутри читает старое значение.
setIntervalсsetCount(count + 1)и пустым массивом навсегда застрянет на единице. ЛечитсяsetCount(c => c + 1)или честными зависимостями; - бесконечный цикл: эффект меняет состояние, от которого зависит, или зависит от объекта и функции, созданных заново на каждом рендере;
- гонка запросов: быстро сменили
id, ответ на старый запрос пришёл последним и перезаписал данные. НуженAbortControllerв cleanup или флагignore; - нет cleanup: подписки, таймеры и слушатели
windowживут после размонтирования; asyncфункция прямо в эффекте: она возвращает промис, а React ждёт функцию очистки. Асинхронный код выносят во внутреннюю функцию;- двойной запуск в разработке: в
StrictModeначиная с React 18 эффект монтируется, размонтируется и монтируется снова. Это проверка cleanup, в продакшене её нет; - мерцание: измерения DOM в
useEffectидут после отрисовки, для синхронной правки нуженuseLayoutEffect.
Сильный ответ заканчивается тем, что многие эффекты не нужны: производное состояние считается при рендере, а загрузку данных лучше отдать TanStack Query или фреймворку.
Где спотыкаются
Отключают правило react-hooks/exhaustive-deps, вместо того чтобы понять, зачем линтер требует зависимость. Баг остаётся, пропадает только предупреждение.
Считают двойной вызов в StrictMode ошибкой React и глушат его флагом в useRef, хотя он честно показывает отсутствующий cleanup.
Синхронизируют в эффекте одно состояние с другим. Получается лишний рендер и рассинхрон, хотя значение можно было просто вычислить.