Как создать бесконечную прокрутку в React с помощью API Intersection Observer
![](/static/storage/21108180928339351554020175384401741419.png)
Привет, коллеги-разработчики! Вы когда-нибудь задумывались, как приложения для социальных сетей, таких как Facebook и Instagram, заставляют вас бесконечно прокручивать свою ленту?
Этот пользовательский интерфейс, предназначенный для загрузки нового контента по требованию, использует технику, называемую бесконечной прокруткой. Это позволяет удерживать вас в этих приложениях часами.
Традиционно для отображения нового контента в этих приложениях нужно было нажать кнопку «Следующая страница». Однако бесконечная прокрутка снижает нагрузку на сервер за счет получения меньшего количества данных за раз и, таким образом, обеспечивает гораздо более увлекательный пользовательский опыт.
В этом посте мы собираемся реализовать ту же функцию на JavaScript. Мы будем использовать API Intersection Observer для загрузки данных по требованию, когда пользователь прокручивает страницу. Мы создадим простое приложение React, которое будет отображать посты, похожие на ленту социальных сетей.
Как настроить приложение React
Запустите create-react-app
в терминале или используйте современный инструмент, например Vite, для создания приложения React. Удалите существующий шаблонный код. Нет необходимости устанавливать дополнительные зависимости. Выполните команду npm start
, чтобы запустить проект.
Полный код этого руководства вы можете найти на GitHub. Давайте приступим.
Как создать функцию извлечения данных
Создайте отдельный файл services.js
и напишите в нем следующую функцию получения данных.
Для получения данных мы будем использовать API /posts
из JSONPlaceholder.
export const fetchPosts = async (page, limit) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=${limit}`
);
const data = await response.json();
return data;
};
Здесь мы передаем API два параметра:
page
указывает на вызываемую часть данных. Она увеличивается каждый раз, когда пользователь прокручивает страницу и загружает новые данные.limit
указывает на количество данных, вызываемых за один раз. Для бесконечной прокрутки мы вызываем только то количество данных, которое может быть отображено на одной странице.
Как создать компонент бесконечной прокрутки
Создадим компонент PostsList
для отображения списка постов с бесконечной прокруткой.
Создадим переменные состояния, получим данные и отобразим их:
const PostsList = () => {
const [posts, setPosts] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
return (
<div>
<h1>Your Feed</h1>
<ul>
{posts.map((post, index) => (
<li
key={post.id}
>
<h2>{post.title}</h2>
<p>{post.body}</p>
</li>
))}
</ul>
{loading && <p>Loading...</p>}
</div>
);
};
Здесь мы определили переменные состояния для постов, номера страницы и состояния загрузки. Обратите внимание, что номер страницы не означает, что мы добавляем пагинацию. Это просто параметр для загрузки следующего набора данных.
Теперь давайте вызовем наш API с номером текущей страницы и установим состояние загрузки:
const loadMorePosts = async () => {
setLoading(true);
const newPosts = await fetchPosts(page, 10);
setPosts((prevPosts) => [...prevPosts, ...newPosts]);
setLoading(false);
};
useEffect(() => {
loadMorePosts();
}, [page]);
До сих пор мы получали первые 10 постов при первой загрузке страницы. Мы хотим загружать больше данных по мере того, как пользователь прокручивает страницу вниз.
Далее мы воспользуемся API Intersection Observer, чтобы определить, когда нужно загрузить дополнительные данные.
Прежде чем использовать его, давайте разберемся, что это за API.
Что такое API Intersection Observer?
Intersection Observer API — это веб-интерфейс, который позволяет асинхронно наблюдать за изменениями в пересечении целевого элемента с предыдущим элементом или областью просмотра.
Проще говоря, он позволяет обнаружить, когда элемент входит или выходит из области другого элемента DOM или области просмотра. Использование Intersection Observer даёт нам следующие преимущества:
- Уменьшает необходимость прикреплять слушателей событий к каждому событию прокрутки.
- Устраняет необходимость в ручных вычислениях позиции элемента и их слушателей событий, упрощая тем самым ваш код.
- Эффективно отслеживает несколько событий по сравнению с прослушивателями событий прокрутки или изменения размера.
Просмотрите MDN Docs, чтобы узнать больше о Intersection Observer.
Как использовать API Intersection Observer
Чтобы использовать этот API, нам нужно создать объект-наблюдатель.
Вот как создать объект-наблюдатель:
const observer = new IntersectionObserver(callback, options);
callback
– это функция, вызываемая при изменении видимости наблюдаемого элемента. Эта функция принимает два аргумента:entries
и сам объектobserver
. Каждый объект в массивеentries
– это объектIntersectionObserverEntry
, который содержит информацию о статусе пересечения наблюдаемого элемента.options
– необязательный аргумент для дальнейшей настройки наблюдателя.
Как использовать это в нашем приложении? Мы определим объект-наблюдатель как ссылку:
const observer = useRef();
Теперь, чтобы установить этот объект-наблюдатель на элемент и определить, пересекается ли этот элемент с областью просмотра, мы используем следующую функцию, которая является обратным вызовом:
const lastPostElementRef = useCallback(
(node) => {
if (loading) return;
if (observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
setPage((prevPage) => prevPage + 1); // trigger loading of new posts by chaging page no
}
});
if (node) observer.current.observe(node);
},
[loading]
);
Давайте разберёмся, как работает эта функция:
- Мы проверяем, загружаются ли ещё данные. Если да, то мы не выполняем логику.
- Для обращения к объекту
observer
мы используем свойствоcurrent
. - Если элемент уже наблюдается, то отключаем его и создаем новый наблюдатель, который изменяет номер страницы, тем самым инициируя вызов API, если наблюдаемый элемент пересекается с областью просмотра.
- Поскольку мы наблюдаем только один элемент за раз (например, последний элемент страницы), размер
entries
равен единице. - Теперь этот новый наблюдатель будет наблюдать за текущим элементом, к которому привязана ссылка, то есть за последним элементом на странице.
Поскольку мы хотим наблюдать только последний элемент на странице, мы добавляем эту ссылку в соответствии со следующим условием:
{posts.map((post, index) => (
<li
key={post.id}
ref={posts.length === index + 1 ? lastPostElementRef : null}
>
...
</li>
))}
Итак, почему мы используем обратные вызовы, а не сам вызов observer
?
Ссылка дает нам прямую ссылку на элемент и устанавливает значение объекта ссылки напрямую. Это хорошо работает для элементов, которым не нужно динамически менять ссылку.
Ссылка с обратным вызовом предоставляет больше контроля над ссылкой и может более эффективно обрабатывать динамические изменения ссылки. Это функция, вызываемая с экземпляром элемента или его DOM-узлом, когда компонент монтируется, и с null
, если он демонтируется.
В нашем случае ссылка меняется динамически, поскольку последний элемент обновляется по мере загрузки новых данных. Мы также можем написать некоторую логику для наблюдения и отсоединения от узла, одновременно устанавливая наш объект ссылки.
Мы также обернем функцию lastPostElementRef
внутри хука useCallback
, чтобы избежать её повторного создания при каждом рендеринге. Мы будем создавать эту функцию только в том случае, если состояние загрузки изменится, когда придет время выполнить функцию.
Запустите ваше приложение с помощью npm start
, перейдите на http://localhost:3000
и откройте вкладку «Network». Прокручивая страницу вниз, вы увидите, что по мере прокрутки страницы появляются новые API-запросы.
![](/static/storage/45564902008809359418624270500819680433.png)
Вы можете найти полный код на GitHub, а также похожие руководства по JavaScript и React.
Заключение
Когда вы внедряете бесконечную прокрутку в свое приложение, она обеспечивает плавный и увлекательный пользовательский интерфейс, подобный тому, что вы видите в популярных приложениях для социальных сетей. Вместо того чтобы переходить по страницам, пользователи могут без усилий прокручивать контент, который загружается динамически.
В нашем примере мы создали имитацию ленты социальных сетей, которая загружала все больше контента по мере того, как пользователь прокручивал страницу вниз. Мы использовали API Intersection Observer для определения положения последнего элемента, в соответствии с которым загружались дополнительные данные. Это упростило наш код и избавило от необходимости подключать несколько слушателей событий.
Надеюсь, это поможет вам создать подобные функции в вашем следующем веб-проекте и позволит обеспечить интересный пользовательский опыт. Пожалуйста, поделитесь своими мыслями и отзывами. Спасибо!