Как реализовать бесконечный скролл
Что проверяют
Здесь чаще смотрят на реализацию в компоненте: как вы отслеживаете конец списка, как не отправить три одинаковых запроса и как не поймать устаревшее состояние в обработчике. Типичный уточняющий вопрос: чем ваш вариант отличается от готового useInfiniteQuery.
Как отвечать
Каркас на React:
- сторожок: пустой
divв конце списка иrefна него. ВuseEffectсоздаётеIntersectionObserver, в колбэке приentry.isIntersectingвызываете загрузку, в функции очистки делаетеobserver.disconnect(); - защита от повторов: флаг «идёт загрузка» держите в
useRef, а не только вuseState. Колбэк наблюдателя замыкает значения на момент создания, и state внутри него может быть устаревшим; - курсор: следующий запрос несёт курсор из прошлого ответа, а
hasMoreотключает наблюдатель, когда данных больше нет; - гонки и отмена: при смене фильтра старые запросы отменяются через
AbortController, иначе ответ по старому фильтру допишется в новый список; - ключи:
keyберётся изidзаписи, а не из индекса, и при склейке страниц стоит отсекать дубли поid.
В рабочем проекте всё это уже есть в TanStack Query: useInfiniteQuery с getNextPageParam, флагами hasNextPage и isFetchingNextPage. Вызов fetchNextPage принято охранять условием hasNextPage && !isFetchingNextPage, иначе повторный вызов перезапустит запрос. Длинный список дополняют виртуализацией через @tanstack/react-virtual, чтобы в DOM жили десятки узлов, а не тысячи.
Где спотыкаются
Вешают scroll в useEffect с пустым массивом зависимостей и проверяют внутри loading из state. Обработчик навсегда видит начальное false, защита не работает, и у низа страницы летит пачка одинаковых запросов.
Забывают снять обработчик или отключить наблюдатель. В строгом режиме React в разработке эффект монтируется дважды, и без очистки получаются два наблюдателя и двойная загрузка.
Запрашивают один и тот же адрес без курсора или номера страницы и склеивают ответ с предыдущим. Список растёт повторами одних и тех же записей, а тест, который проверяет только индикатор загрузки, этого не заметит.