Зачем из useEffect возвращать callback и когда он вызывается
Что проверяют
Знаете ли вы, что это функция очистки, и понимаете ли, что она вызывается не только при размонтировании.
Как отвечать
Возвращаемая функция это очистка. React вызывает её в двух случаях:
- перед следующим запуском эффекта, если изменились зависимости;
- при размонтировании компонента.
Второй пункт помнят все, первый забывают, и именно из-за него возникают дефекты: подписка накапливается на каждом изменении зависимости.
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, [delay]);Без очистки при смене delay старый интервал продолжит работать, и через несколько изменений их станет пять.
Что обязательно очищать: подписки на события, интервалы и таймеры, соединения (сокет, слушатель хранилища), запросы через AbortController, подписки на внешние источники.
Важная деталь про замыкание: очистка видит значения того же рендера, в котором был создан эффект. Поэтому она снимает именно ту подписку, которую поставила, а не последнюю по счёту. Это делает поведение предсказуемым и позволяет думать про каждый рендер отдельно.
Про режим строгой проверки: в разработке React монтирует компонент дважды и, соответственно, выполняет эффект, очистку и снова эффект. Это не ошибка, а проверка того, что очистка написана правильно. Если после двойного вызова что-то ломается или запрос уходит дважды и это заметно, значит очистки не хватает.
Что вернуть нельзя: промис. Поэтому колбэк эффекта не делают асинхронным, а асинхронную функцию объявляют и вызывают внутри.
Где спотыкаются
Думают, что очистка вызывается только при размонтировании.
Не отписываются и получают дублирующиеся обработчики.
Пишут useEffect(async () => ...) и теряют возможность очистки.
Считают двойной вызов в разработке багом React.