Что функция, передаваемая в useEffect, может возвращать и зачем
Что проверяют
Понимаете ли вы, что возвращаемое значение эффекта это функция очистки, и когда именно React её вызывает. Следом обычно спрашивают, почему эффект нельзя объявить async и зачем в режиме разработки эффект срабатывает дважды.
Как отвечать
Вариантов ровно два:
- ничего (
undefined): эффекту нечего убирать, например он просто обновляетdocument.title; - функцию очистки: она снимает то, что эффект создал. Типичные случаи:
clearIntervalиclearTimeout,removeEventListener, отписка от WebSocket или стора, отмена запроса черезAbortController.
Любое другое значение React не ожидает и в режиме разработки пишет предупреждение в консоль. Отсюда ответ про async: асинхронная функция возвращает промис, а не функцию очистки. Правильно объявить асинхронную функцию внутри эффекта и вызвать её, а в очистке отменить запрос или выставить флаг ignore, чтобы устаревший ответ не записался в состояние.
Когда вызывается очистка:
- перед повторным запуском эффекта, если изменились зависимости. Очистка видит значения из того рендера, в котором эффект был создан, а не текущие;
- при размонтировании компонента;
- в
StrictModeпри разработке: начиная с React 18 компонент монтируется, сразу проходит очистку и монтируется снова. Так React проверяет, что эффект симметричен. В продакшн-сборке этого нет.
Хорошо добавить, что в React 19 функцию очистки может возвращать и callback-ref: это удобно, когда нужно подписаться на DOM-узел без отдельного эффекта.
Где спотыкаются
Пишут useEffect(async () => {...}). Промис попадает туда, где React ждёт функцию очистки, и отменить запрос у такого эффекта нечем.
Считают, что очистка вызывается только при размонтировании, как componentWillUnmount. На деле она срабатывает перед каждым перезапуском эффекта, и без неё подписки и таймеры накапливаются с каждым изменением зависимостей.
Лечат двойной запуск в StrictMode флагом в useRef вроде «уже запускали». Это прячет баг, а не чинит его: правильное решение в том, чтобы написать нормальную очистку.