> Как реализовать бесконечную ленту (infinite scroll) в веб-приложении (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: YADRO
Стек: JavaScript
> Пример ответа
Короткий ответ
Infinite scroll реализуется через Intersection Observer API или событие scroll с троттлингом. При достижении триггерного элемента (обычно последнего в списке) отправляется запрос на сервер за следующей порцией данных, которые добавляются в DOM. Ключевые моменты: управление состоянием пагинации, обработка ошибок, предотвращение дублирования запросов и корректная работа с памятью при большом количестве элементов.
Подробное объяснение
Infinite scroll - это паттерн пагинации, где новый контент подгружается автоматически при прокрутке страницы вниз. Основные компоненты реализации:
-
Детекция триггерной точки: используется Intersection Observer (современный подход) или scroll event с троттлингом. Observer наблюдает за элементом-маяком, расположенным в конце списка.
-
Управление состоянием: храним текущую страницу, флаг загрузки, флаг окончания данных (hasMore), возможные ошибки. Это критично для предотвращения множественных запросов.
-
Асинхронная загрузка: при срабатывании триггера отправляется запрос к API с параметрами пагинации (page, limit, cursor). После получения ответа данные добавляются к существующему списку.
-
Обработка граничных случаев: пустой ответ (конец данных), ошибка сети, быстрая прокрутка (несколько срабатываний триггера), удаление элементов из середины списка.
-
Оптимизация производительности: виртуализация списка (react-window, virtual-scroller) для больших наборов данных, debounce для scroll-событий, очистка observer при размонтировании.
На практике
В production-приложениях используют готовые решения или строят на основе Intersection Observer. Типичная архитектура:
- Компонент-контейнер управляет состоянием и рендерит список
- Компонент-маяк (sentinel) размещается после последнего элемента
- Хук useInfiniteScroll инкапсулирует логику observer и вызовов API
- Для cursor-based пагинации передаётся идентификатор последнего элемента
- Обработка ошибок: показываем кнопку "Повторить" вместо автоматической загрузки
- Для SEO и accessibility добавляем fallback в виде обычной пагинации
Пример кода
JAVASCRIPT// Хук для infinite scrollfunction useInfiniteScroll({ fetchMore, hasMore }) {const [loading, setLoading] = useState(false);const [error, setError] = useState(null);const sentinelRef = useRef(null);useEffect(() => {if (!hasMore) return;const observer = new IntersectionObserver(async ([entry]) => {if (entry.isIntersecting && !loading) {setLoading(true);setError(null);try {await fetchMore();} catch (err) {setError(err);} finally {setLoading(false);}}},{ rootMargin: '200px' });const sentinel = sentinelRef.current;if (sentinel) observer.observe(sentinel);return () => {if (sentinel) observer.unobserve(sentinel);observer.disconnect();};}, [hasMore, loading, fetchMore]);return { sentinelRef, loading, error };}// Использованиеfunction Feed() {const [items, setItems] = useState([]);const [page, setPage] = useState(1);const [hasMore, setHasMore] = useState(true);const fetchMore = async () => {const response = await api.getItems({ page, limit: 20 });setItems(prev => [...prev, ...response.data]);setHasMore(response.data.length === 20);setPage(prev => prev + 1);};const { sentinelRef, loading, error } = useInfiniteScroll({fetchMore,hasMore});return (<div>{items.map(item => <Item key={item.id} data={item} />)}{loading && <Spinner />}{error && <button onClick={fetchMore}>Повторить</button>}{hasMore && <div ref={sentinelRef} style={{ height: 1 }} />}</div>);}
Как отвечать на собеседовании
Начни с ключевого механизма - Intersection Observer. Объясни, почему это лучше scroll-событий (производительность, точность). Затем перейди к управлению состоянием: как предотвращаешь race conditions, обрабатываешь ошибки, определяешь конец данных. Упомяни trade-off между offset и cursor пагинацией. Для senior-уровня добавь про виртуализацию, работу с памятью при тысячах элементов и SEO-аспекты.
Что проверяет интервьюер
- Понимание асинхронных потоков и управления состоянием
- Знание современных API браузера (Intersection Observer)
- Умение проектировать компоненты с учётом граничных случаев
- Навыки оптимизации производительности (троттлинг, виртуализация)
- Понимание UX-паттернов и accessibility
Типичные ошибки
- Использование scroll-события без троттлинга, что вызывает лаги
- Отсутствие флага загрузки - множественные запросы при быстрой прокрутке
- Неправильная очистка observer при размонтировании компонента
- Игнорирование ошибок сети - пользователь видит вечную загрузку
- Отсутствие fallback для пользователей с отключённым JS или screen reader
- Утечка памяти при бесконечном добавлении элементов без виртуализации
> Похожие задачи по frontend
Если объект является экземпляром класса и методы лежат в прототипе, где их увидим: в for-in или Object.keys
Как реализовать перетаскивание модального окна мышкой на чистом JavaScript с использованием событий?
Что представляет браузер и какие объекты он предоставляет
Что такое среда исполнения JavaScript
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью