Бесконечная прокрутка — это популярный шаблон пользовательского интерфейса, который позволяет пользователям динамически загружать и отображать контент по мере прокрутки страницы. Этот метод удобен для отображения больших наборов данных, таких как результаты поиска или каналы социальных сетей, не перегружая пользователя слишком большим количеством информации сразу. В этой статье мы рассмотрим, как реализовать бесконечную прокрутку в приложении React.

Мы будем использовать Typescript, потому что он позволяет нам лучше понять мою кодовую базу, писать более чистый код и уверенно масштабировать мои приложения.

Откройте этот репозиторий https://github.com/stevelukis/react-inifinite-scroll и клонируйте его.

git clone https://github.com/stevelukis/react-inifinite-scroll.git

Мы будем использовать каталог start в качестве основы для работы.

cd start

Установите все необходимые зависимости.

pnpm install

Запустите веб-сервер.

pnpm start

Все должно работать гладко, и вы увидите это в браузере:

Это поддельная домашняя страница со списком продуктов. Мы увидим, как извлекать данные из API продукта с разбивкой на страницы, но данные будут загружаться каждый раз, когда пользователи прокручивают страницу вниз (вместо того, чтобы нажимать «следующая страница»).

В этой статье мы будем использовать React Query. React Query — это библиотека, которая упрощает управление данными в приложениях React, предоставляя упрощенный способ извлечения и кэширования данных из API и других источников.

Давайте установим его.

pnpm install @tanstack/[email protected]

Затем установите react-infinite-scroll-component.

pnpm install [email protected]

Для выполнения сетевых запросов будет использоваться axios, так что давайте его тоже установим.

pnpm install [email protected]

Откройте src/App.tsx . Над компонентом App создайте экземпляр QueryClient.

const client = new QueryClient();

Затем оберните HomePage внутри QueryClientProvider.

...
      <ChakraProvider>
        <QueryClientProvider client={client}>
          <HomePage />
        </QueryClientProvider>
      </ChakraProvider>
...

Смысл этого в том, чтобы каждый компонент в дереве компонентов мог получить доступ к клиенту запроса, как при использовании контекста React. Но это не приведет к ненужному повторному рендерингу, потому что значение client стабильно (вот почему мы определяем его вне компонента App!).

Для простоты мы будем использовать поддельный API от dummyjson.com. Это пример ответа для продукта:

{
    "products": [
        {
            "id": 1,
            "title": "iPhone 9",
            "description": "An apple mobile which is nothing like apple",
            "price": 549,
            "discountPercentage": 12.96,
            "rating": 4.69,
            "stock": 94,
            "brand": "Apple",
            "category": "smartphones",
            "thumbnail": "https://i.dummyjson.com/data/products/1/thumbnail.jpg",
            "images": [
                "https://i.dummyjson.com/data/products/1/1.jpg",
                "https://i.dummyjson.com/data/products/1/2.jpg",
                "https://i.dummyjson.com/data/products/1/3.jpg",
                "https://i.dummyjson.com/data/products/1/4.jpg",
                "https://i.dummyjson.com/data/products/1/thumbnail.jpg"
            ]
        },
        ...
        ...
    ]
    "total": 100,
    "skip": 0,
    "limit": 30
}

В каталоге src создайте файл с именем hooks.ts. Мы создадим интерфейсы, определяющие форму ответа. Напишите этот код:

export interface Product {
  id: number;
  title: string;
  price: number;
  images: string[];
}

interface FetchResponse {
  products: Product[];
  skip: number;
}

API возвращает 30 элементов на страницу по умолчанию, мы будем следовать этому.

const PAGE_SIZE = 30;

Затем мы собираемся создать хук, который будет запрашивать данные из API. В начале файла добавьте эти импорты:

import axios from "axios";
import { useInfiniteQuery } from "@tanstack/react-query";

useInfiniteQuery полезен для управления бесконечной прокруткой данных в приложениях React, поскольку он обеспечивает удобный способ извлечения и кэширования данных небольшими партиями по мере того, как пользователь прокручивает страницу вниз.

Под интерфейсом Product создайте хук.

const useProducts = () =>
  useInfiniteQuery({
    queryKey: ["products"],
    queryFn: ({ pageParam = 1 }) =>
      axios
        .get<FetchResponse>("https://dummyjson.com/products", {
          params: {
            skip: (pageParam - 1) * PAGE_SIZE,
          },
        })
        .then((res) => res.data.products),
    getNextPageParam: (lastPage, allPages) =>
      lastPage.length === 0 ? undefined : allPages.length + 1,
  });
  • queryKey — это уникальный идентификатор запросов, позволяющий эффективно кэшировать данные и предотвращать конфликты кеша.
  • queryFn определяет, как данные извлекаются и преобразуются. У него есть параметр pageParam, который можно использовать для получения любой страницы данных. API использует skip для управления нумерацией страниц. Таким образом, если на первой странице показано 30 элементов, мы можем получить вторую страницу, пропустив первые 30 элементов.
  • getNextPageParam решает, будет ли следующая страница или нет. Если на последней странице, которую мы извлекаем, нет данных, это означает, что больше нет страницы для выборки, и мы должны вернуть undefined, чтобы React Query знал.

Сделайте useProducts экспортом по умолчанию, добавив это в конец файла:

export default useProducts;

Ваш hooks.ts должен выглядеть так:

Теперь откройте src/components/HomePage.tsx. Добавьте эти импорты:

import useProducts from "../hooks";
import InfiniteScroll from "react-infinite-scroll-component";

Замените этот фиктивный массив:

const items = [...Array(20)];

с материалами из нашего крючка:

const { data, fetchNextPage, hasNextPage } = useProducts();
  • data содержит массив страниц страниц товаров.
  • fetchNextPage — это триггерная функция, которая при вызове заставит React Query получить следующую страницу, если она существует.
  • hasNextPage — логическое значение, указывающее, есть ли следующая страница.

Затем оберните SimpleGrid компонентом InfiniteScroll.

<InfiniteScroll
    next={fetchNextPage}
    hasMore={hasNextPage || false}
    loader={<Text>Loading...</Text>}
    dataLength={
        data?.pages.reduce((total, page) => total + page.length, 0) ||
        0
    }
>
    <SimpleGrid>...</SimpleGrid>
</InfiniteScroll>

react-infinite-scroll-component — это полезная библиотека для реализации бесконечной прокрутки в приложениях React, упрощающая процесс обнаружения и запуска новых загрузок данных при прокрутке пользователем.

Замена детей SimpleGrid:

<SimpleGrid columns={{ sm: 2, md: 5 }} gap={2}>
    {data?.pages.map((page) =>
        page.map((product) => <ProductCard key={product.id} />)
    )}
</SimpleGrid>

Снова запустите веб-сервер и посмотрите результат:

Количество данных увеличивается по мере того, как мы прокручиваем вниз, пока данные для извлечения не закончатся.

Теперь давайте обновим компонент ProductCard, чтобы мы могли передавать информацию о продукте. Откройте src/components/ProductCard.tsx. Добавьте импорт:

import { Product } from "../hooks";

Создадим интерфейс для реквизита:

interface Props {
  product: Product;
}

Затем создайте параметр реквизита:

const ProductCard = ({ product }: Props) => {
  .
  .
  .
}

Теперь у нас есть product в компоненте, поэтому мы можем использовать его данные вместо поддельных данных. Замените изображение, заголовок и цену.

<Image
  src={product.images[0]}
  rounded="lg"
/>
<Heading size="md">{product.title}</Heading>
<Text>${product.price}</Text>

Вернуться к компоненту HomePage. Передайте товар ProductCard.

<ProductCard key={product.id} product={product} />

ProductCard.tsx:

HomePage.tsx:

Запустите веб-сервер еще раз, теперь вы должны увидеть его полностью функциональным:

Как видно из нашего рабочего примера, реализация бесконечной прокрутки в приложении React с помощью React Query и react-infinite-scroll-component может быть простым и эффективным решением для управления большими наборами данных. Объединив эти инструменты с другими передовыми методами управления данными и дизайна взаимодействия с пользователем, вы можете создавать быстрые и быстро реагирующие приложения, обеспечивающие бесперебойную работу для ваших пользователей.

Полный код доступен здесь:

https://github.com/stevelukis/react-inifinite-scroll/tree/main/finish

Бесконечная прокрутка — это мощный метод улучшения взаимодействия с пользователем в приложениях, отображающих большие наборы данных. Загружая и отображая контент динамически по мере прокрутки страницы, мы можем не перегружать пользователя слишком большим объемом информации за один раз. Благодаря React Query и react-infinite-scroll-component реализовать бесконечную прокрутку в приложении React стало еще проще. Упрощая процесс выборки и кэширования данных, а также обнаруживая и запуская новые загрузки данных по мере прокрутки пользователем, эти библиотеки помогают упростить процесс разработки и повысить производительность и масштабируемость наших приложений. Мы надеемся, что эта статья предоставила вам полезное введение в реализацию бесконечной прокрутки в React, и что вы найдете эти инструменты полезными в своих будущих проектах.