Как реализовать бесконечную прокрутку и нумерацию страниц с помощью Next.js и запроса TanStack

7
компьютеры и технологии 22.webp.webp

Последнее обновление 18.11.2023 — Василий Иванов

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

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

Пагинация и бесконечная прокрутка с использованием запроса TanStack

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

Разбивка на страницы предполагает разделение большого набора данных на более мелкие страницы, что позволяет пользователям перемещаться по содержимому управляемыми фрагментами с помощью кнопок навигации. Напротив, бесконечная прокрутка обеспечивает более динамичный просмотр. По мере прокрутки пользователем новые данные загружаются и отображаются автоматически, что устраняет необходимость в явной навигации.

По теме:  Как настроить Linux и приложения Linux на ПК с Windows

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

Вы можете найти код этого проекта в этом репозитории GitHub.

Настройка проекта Next.js

Для начала создайте проект Next.js. Установите последнюю версию Next.js 13, которая использует каталог приложений.

 npx create-next-app@latest next-project --app 

Затем установите пакет TanStack в свой проект с помощью npm, менеджера пакетов Node.

 npm i @tanstack/react-query 

Интегрируйте запрос TanStack в приложение Next.js

Чтобы интегрировать TanStack Query в ваш проект Next.js, вам необходимо создать и инициализировать новый экземпляр TanStack Query в корне приложения — файле layout.js. Для этого импортируйте QueryClient и QueryClientProvider из TanStack Query. Затем оберните дочернюю опору QueryClientProvider следующим образом:

 "use client"
import React from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
};

export default function RootLayout({ children }) {
  const queryClient = new QueryClient();

  return (
    <html lang="en">
      <body>
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      </body>
    </html>
  );
}

export { metadata };

Эта настройка гарантирует, что TanStack Query имеет полный доступ к состоянию приложения.

Реализация нумерации страниц с помощью хука useQuery

Хук useQuery упрощает получение данных и управление ими. Предоставляя параметры нумерации страниц, такие как номера страниц, вы можете легко получить определенные подмножества данных.

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

Теперь, чтобы реализовать нумерацию страниц в приложении Next.js, создайте файл Pagination/page.js в каталоге src/app. Внутри этого файла выполните следующий импорт:

 "use client"
import React, { useState } from 'react';
import { useQuery} from '@tanstack/react-query';
import './page.styles.css';

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

 export default function Pagination() {
  const [page, setPage] = useState(1);

  const fetchPosts = async () => {
    try {
      const response = await fetch(`https://jsonplaceholder.typicode.com/posts?
                                  _page=${page}&_limit=10`);

      if (!response.ok) {
        throw new Error('Failed to fetch posts');
      }

      const data = await response.json();
      return data;
    } catch (error) {
      console.error(error);
      throw error;
    }
  };

  // add the following code here
}

Теперь определите хук useQuery и укажите следующие параметры в качестве объектов:

   const { isLoading, isError, error, data } = useQuery({
    keepPreviousData: true,
    queryKey: ['posts', page],
    queryFn: fetchPosts,
  });

Значение KeepPreviousData имеет значение true, что гарантирует, что при получении новых данных приложение сохранит предыдущие данные. Параметр queryKey — это массив, содержащий ключ запроса, в данном случае конечную точку и текущую страницу, для которой вы хотите получить данные. Наконец, параметр queryFn, fetchPosts, запускает вызов функции для получения данных.

Как упоминалось ранее, перехватчик предоставляет несколько состояний, которые вы можете распаковать, подобно тому, как вы деструктурируете массивы и объекты, и использовать их для улучшения взаимодействия с пользователем (рендеринг соответствующих пользовательских интерфейсов) во время процесса выборки данных. К этим состояниям относятся isLoading, isError и другие.

Для этого включите следующий код для отображения различных экранов сообщений в зависимости от текущего состояния текущего процесса:

   if (isLoading) {
    return (<h2>Loading...</h2>);
  }

  if (isError) {
    return (<h2 className="error-message">{error.message}</h2>);
  }

Наконец, включите код элементов JSX, которые будут отображаться на странице браузера. Этот код также выполняет две другие функции:

  • Как только приложение получит сообщения из API, они будут сохранены в переменной данных, предоставленной ловушкой useQuery. Эта переменная помогает управлять состоянием приложения. Затем вы можете сопоставить список сообщений, хранящихся в этой переменной, и отобразить их в браузере.
  • Добавить две кнопки навигации, «Предыдущий» и «Далее», чтобы пользователи могли запрашивать и отображать дополнительные данные с разбивкой на страницы соответствующим образом.
   return (
    <div>
      <h2 className="header">Next.js Pagination</h2>
      {data && (
        <div className="card">
          <ul className="post-list">
            {data.map((post) => (
                <li key={post.id} className="post-item">{post.title}</li>
            ))}
          </ul>
        </div>
      )}
      <div className='btn-container'>
        <button
          onClick={() => setPage(prevState => Math.max(prevState - 1, 0))}
          disabled={page === 1}
          className="prev-button"
        >Prev Page</button>

        <button
          onClick={() => setPage(prevState => prevState + 1)}
          className="next-button"
        >Next Page</button>
      </div>
    </div>
  );

Наконец, запустите сервер разработки.

 npm run dev 

Затем перейдите по адресу http://localhost:3000/Pagination в браузере.

Поскольку вы включили папку Pagination в каталог приложения, Next.js рассматривает ее как маршрут, позволяя вам получить доступ к странице по этому URL-адресу.

Бесконечная прокрутка с использованием хука useInfiniteQuery

Бесконечная прокрутка обеспечивает удобство просмотра. Хорошим примером является YouTube, который автоматически загружает новые видео и отображает их при прокрутке вниз.

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

Чтобы реализовать бесконечную прокрутку, добавьте файл InfiniteScroll/page.js в каталог src/app. Затем выполните следующий импорт:

 "use client"
import React, { useRef, useEffect, useState } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import './page.styles.css';

Затем создайте функциональный компонент React. Внутри этого компонента, аналогично реализации нумерации страниц, создайте функцию, которая будет получать данные сообщений.

 export default function InfiniteScroll() {
  const listRef = useRef(null);
  const [isLoadingMore, setIsLoadingMore] = useState(false);

  const fetchPosts = async ({ pageParam = 1 }) => {
    try {
      const response = await fetch(`https://jsonplaceholder.typicode.com/posts?
                                  _page=${pageParam}&_limit=5`);

      if (!response.ok) {
        throw new Error('Failed to fetch posts');
      }

      const data = await response.json();
      await new Promise((resolve) => setTimeout(resolve, 2000));
      return data;
    } catch (error) {
      console.error(error);
      throw error;
    }
  };

  // add the following code here
}

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

Теперь определите хук useInfiniteQuery. Когда компонент первоначально монтируется, перехватчик извлекает первую страницу данных с сервера. Когда пользователь прокручивает страницу вниз, хук автоматически извлекает следующую страницу данных и отображает ее в компоненте.

   const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
    getNextPageParam: (lastPage, allPages) => {
      if (lastPage.length < 5) {
        return undefined;
      }
      return allPages.length + 1;
    },
  });

  const posts = data ? data.pages.flatMap((page) => page) : [];

Переменная messages объединяет все сообщения с разных страниц в один массив, в результате чего получается сглаженная версия переменной данных. Это позволяет вам легко отображать и отображать отдельные сообщения.

Чтобы отслеживать прокрутку пользователя и загружать больше данных, когда пользователь приближается к нижней части списка, вы можете определить функцию, которая использует API Intersection Observer API для определения момента пересечения элементов с областью просмотра.

   const handleIntersection = (entries) => {
    if (entries[0].isIntersecting && hasNextPage && !isFetching && !isLoadingMore) {
      setIsLoadingMore(true);
      fetchNextPage();
    }
  };

  useEffect(() => {
    const observer = new IntersectionObserver(handleIntersection, { threshold: 0.1 });

    if (listRef.current) {
      observer.observe(listRef.current);
    }

    return () => {
      if (listRef.current) {
        observer.unobserve(listRef.current);
      }
    };
  }, [listRef, handleIntersection]);

  useEffect(() => {
    if (!isFetching) {
      setIsLoadingMore(false);
    }
  }, [isFetching]);

Наконец, включите элементы JSX для сообщений, которые отображаются в браузере.

   return (
    <div>
      <h2 className="header">Infinite Scroll</h2>
      <ul ref={listRef} className="post-list">
        {posts.map((post) => (
          <li key={post.id} className="post-item">
            {post.title}
          </li>
        ))}
      </ul>
      <div className="loading-indicator">
        {isFetching ? 'Fetching...' : isLoadingMore ? 'Loading more...' : null}
      </div>
    </div>
  );

После внесения всех изменений посетите http://localhost:3000/InfiniteScroll, чтобы увидеть их в действии.

TanStack Query: больше, чем просто получение данных

Пагинация и бесконечная прокрутка — хорошие примеры, подчеркивающие возможности TanStack Query. Проще говоря, это универсальная библиотека управления данными.

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