Jak implementovat nekonečné posouvání a stránkování pomocí Next.js a TanStack Query

Většina aplikací, které vyvíjíte, pracuje s daty, a s rostoucí složitostí programů roste i objem těchto dat. Pokud aplikace nedokážou efektivně spravovat velká množství dat, jejich výkonnost se snižuje.

Pro optimalizaci výkonu aplikací se často používají dvě techniky: stránkování a nekonečné posouvání. Tyto metody pomáhají efektivněji zpracovávat vykreslování dat a zlepšují celkový dojem uživatele.

Stránkování a nekonečné posouvání s využitím TanStack Query

TanStack Query, adaptace React Query, je robustní knihovna pro správu stavu v JavaScriptových aplikacích. Nabízí efektivní řešení pro správu stavu aplikace, včetně funkcí pro práci s daty, jako je ukládání do mezipaměti.

Stránkování spočívá v rozdělení velké datové sady na menší stránky, což uživatelům umožňuje procházet obsah po částech pomocí navigačních prvků. Na druhou stranu, nekonečné posouvání poskytuje dynamičtější zážitek z prohlížení. Jak uživatel posouvá, nová data se automaticky načítají a zobrazují, bez nutnosti explicitní navigace.

Obě techniky, stránkování i nekonečné posouvání, slouží k efektivní správě a prezentaci velkých objemů dat. Volba mezi nimi závisí na specifických potřebách aplikace.

Kód tohoto projektu je k dispozici v úložišti GitHub.

Založení projektu Next.js

Pro začátek si vytvořte nový projekt Next.js. Použijte nejnovější verzi Next.js 13, která využívá adresář App.

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

Následně nainstalujte balíček TanStack do vašeho projektu pomocí správce balíčků npm.

npm i @tanstack/react-query

Integrace TanStack Query do aplikace Next.js

Pro integraci TanStack Query do projektu Next.js je potřeba vytvořit a inicializovat novou instanci TanStack Query v kořenovém adresáři aplikace – v souboru layout.js. Importujte QueryClient a QueryClientProvider z TanStack Query. Poté obalte dětskou rekvizitu pomocí 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 };

Toto nastavení zajistí, že TanStack Query bude mít plný přístup ke stavu aplikace.

Hook useQuery usnadňuje načítání a správu dat. Pomocí parametrů stránkování, jako jsou čísla stránek, můžete snadno načítat konkrétní podmnožiny dat.

Navíc, tento hook poskytuje různé možnosti a konfigurace pro přizpůsobení načítání dat, včetně nastavení možností mezipaměti a efektivního zpracování stavů načítání. S těmito funkcemi můžete bez námahy implementovat bezproblémové stránkování.

Pro implementaci stránkování v aplikaci Next.js vytvořte soubor Pagination/page.js v adresáři src/app. V tomto souboru proveďte následující importy:

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

Dále definujte funkční komponentu React. V ní musíte definovat funkci pro získávání dat z externího API. V tomto případě se použije JSONPlaceholder API pro načtení sady příspěvků.

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('Nepodařilo se načíst příspěvky');       }

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

   }

Nyní definujte hook useQuery a zadejte následující parametry jako objekty:

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

Hodnota keepPreviousData je true, což zajišťuje, že aplikace při načítání nových dat zachová předchozí data. Parametr queryKey je pole obsahující klíč dotazu, v tomto případě koncový bod a aktuální stránku, pro kterou chcete načíst data. Parametr queryFn, fetchPosts, spouští funkci pro načtení dat.

Jak bylo zmíněno, hook poskytuje několik stavů, které lze rozbalit, podobně jako byste destruovali pole a objekty, a využít je ke zlepšení uživatelské zkušenosti (vykreslování vhodných uživatelských rozhraní) během načítání dat. Tyto stavy zahrnují isLoading, isError a další.

Pro vykreslení různých zpráv na základě aktuálního stavu procesu načítání zahrňte následující kód:

  if (isLoading) {
    return (<h2>Načítání...</h2>);
  }

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

Nakonec zahrňte kód pro prvky JSX, které se vykreslí na stránce prohlížeče. Tento kód slouží i dvěma dalším účelům:

  • Jakmile aplikace načte příspěvky z API, budou uloženy v datové proměnné poskytované hookem useQuery. Tato proměnná pomáhá spravovat stav aplikace. Následně můžete namapovat seznam příspěvků uložených v této proměnné a vykreslit je v prohlížeči.
  • Pro přidání dvou navigačních tlačítek „Předchozí“ a „Další“, aby uživatelé mohli procházet stránkovaná data.
  return (
    <div>
      <h2 className="header">Stránkování Next.js</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"         >Předchozí stránka</button>

        <button           onClick={() => setPage(prevState => prevState + 1)}           className="next-button"         >Další stránka</button>       </div>     </div>   );

Nakonec spusťte vývojový server.

npm run dev

Poté přejděte v prohlížeči na adresu http://localhost:3000/Pagination.

Protože jste do adresáře aplikace zahrnuli složku Pagination, Next.js s ní zachází jako s trasou, která vám umožňuje přístup ke stránce na dané URL adrese.

Nekonečné posouvání poskytuje plynulé procházení. Dobrým příkladem je YouTube, který automaticky načítá nová videa a zobrazuje je při posouvání dolů.

Hook useInfiniteQuery umožňuje implementovat nekonečné posouvání načítáním dat ze serveru po stránkách a automatickým načítáním a vykreslováním další stránky dat, když uživatel posouvá dolů.

Pro implementaci nekonečného posouvání přidejte soubor InfiniteScroll/page.js do adresáře src/app. Poté proveďte následující importy:

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

Dále vytvořte funkční komponentu React. V ní, podobně jako v implementaci stránkování, vytvořte funkci pro načítání dat příspěvků.

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('Nepodařilo se načíst příspěvky');       }

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

   }

Na rozdíl od implementace stránkování, tento kód zavádí dvousekundové zpoždění při načítání dat, aby umožnil uživateli prohlížet aktuální data, zatímco posouvá a spouští opětovné načtení nové sady dat.

Nyní definujte hook useInfiniteQuery. Když se komponenta poprvé připojí, hook načte první stránku dat ze serveru. Když uživatel posouvá dolů, hook automaticky načte další stránku dat a vykreslí ji v komponentě.

  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) : [];

Proměnná posts kombinuje všechny příspěvky z různých stránek do jednoho pole, což vede ke sloučené verzi datové proměnné. To vám umožní snadno mapovat a vykreslovat jednotlivé příspěvky.

Pro sledování rolování uživatele a načítání dalších dat, když se uživatel blíží ke konci seznamu, můžete definovat funkci, která využívá rozhraní Intersection Observer API ke zjištění, kdy se prvky protínají s výřezem.

  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]);

Nakonec zahrňte prvky JSX pro příspěvky, které se vykreslují v prohlížeči.

  return (
    <div>
      <h2 className="header">Nekonečné posouvání</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 ? 'Načítání...' : isLoadingMore ? 'Načítání dalších...' : null}       </div>     </div>   );

Po provedení všech změn navštivte http://localhost:3000/InfiniteScroll a uvidíte je v akci.

TanStack Query: Více než jen načítání dat

Stránkování a nekonečné posouvání jsou skvělé příklady, které ilustrují možnosti TanStack Query. Jednoduše řečeno, jedná se o všestrannou knihovnu pro správu dat.

Díky rozsáhlé sadě funkcí můžete zefektivnit procesy správy dat vaší aplikace, včetně efektivního zpracování stavu. Kromě mnoha dalších úkolů spojených s daty můžete zlepšit celkový výkon vašich webových aplikací i uživatelskou zkušenost.