Hur man implementerar oändlig rullning och paginering med Next.js och TanStack Query
De flesta appar du utvecklar hanterar data, och i takt med att programmen skalas upp kan mängden data bli allt större. När applikationer inte lyckas hantera stora datamängder på ett effektivt sätt presterar de dåligt.
Att använda paginering och oändlig rullning är praktiska metoder för att optimera applikationseffektiviteten, vilket möjliggör förbättrad hantering av datarendering och samtidigt förbättrar den övergripande användarupplevelsen.
Paginering och oändlig rullning med TanStack Query
TanStack Query - “en anpassning av React Query” - är ett robust state management-bibliotek för JavaScript-applikationer. Det erbjuder en effektiv lösning för hantering av applikationsstatus, bland andra funktioner, inklusive datarelaterade uppgifter som cachelagring.
Paginering är en metod för att organisera stora datamängder genom att dela upp dem i mindre, lättnavigerade sektioner med hjälp av pagineringskontroller. Å andra sidan erbjuder oändlig scrollning en adaptiv surfmetod där ytterligare information laddas och visas sömlöst när användaren scrollar nedåt, vilket gör att direktnavigering inte behövs.
Paginering och oändlig scrollning är båda metoder för att hantera och visa stora mängder information på ett användarvänligt sätt, var och en med sina egna fördelar och nackdelar beroende på de specifika behoven i en applikation.
Du hittar källkoden för detta projekt i det GitHub-arkiv som är avsett för det.
Skapa ett Next.js-projekt
För att påbörja processen skapar du ett Next.js-projekt genom att installera den senaste versionen, version 13, som använder en “App”-katalog som grund.
npx create-next-app@latest next-project --app
För att fortsätta måste du installera TanStack-paketet i ditt projekt med hjälp av npm, en allmänt använd pakethanterare för Node.js-applikationer.
npm i @tanstack/react-query
Integrera TanStack Query i Next.js-applikationen
För att integrera TanStack Query i ditt Next.js-projekt är det nödvändigt att skapa och initiera en ny instans av TanStack Query i applikationens kärna - specifikt i filen layout.js. Detta kan uppnås genom att importera både QueryClient och QueryClientProvider från TanStack Query. Därefter omger du barnets egenskap med QueryClientProvider, strukturerad enligt nedan:
"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 };
Denna konfiguration ger TanStack Query full behörighet att förstå och manipulera den aktuella statusen för programvaran.
Implementera paginering med hjälp av useQuery Hook
Användningen av useQuery hook underlättar effektiv hämtning och hantering av data genom att införliva pagineringsattribut, inklusive sidnummer, för att smidigt få fram riktade delar av informationen.
Advanced Repeat är mycket mångsidigt och erbjuder en rad valmöjligheter för att skräddarsy din datahämtningsprocess enligt dina specifika krav. Detta inkluderar möjligheten att skapa cachningsinställningar och enkelt hantera laddningsförhållanden, vilket resulterar i ett smidigt och enhetligt pagineringsresultat.
För att införliva paginering i vår Next.js-applikation ska vi skapa en Pagination/page.js-fil som ligger i källkodens “src/app”-katalog. Denna fil kommer att innehålla de importuttalanden som krävs för att implementera pagineringsfunktionen.
"use client"
import React, { useState } from 'react';
import { useQuery} from '@tanstack/react-query';
import './page.styles.css';
Definiera sedan en funktionell React-komponent. I den här komponenten måste du definiera en funktion som hämtar data från ett externt API. I det här fallet använder du JSONPlaceholder API för att hämta en uppsättning inlägg.
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
kroken kan definieras genom att ange ett objekt med flera nyckel-värdepar som representerar de olika parametrar som krävs för att hämta data från ett GraphQL API. Dessa parametrar kan inkludera själva frågesträngen, variabler för att anpassa frågan, alternativ för att kontrollera hur data hämtas och uppdateras samt retry-policyer i händelse av nätverksfel eller andra problem. Genom att tillhandahålla dessa parametrar inom ramen för useQuery
kroken, gör vi det möjligt för React-komponentträdet att hantera det tillstånd som är associerat med frågans livscykel, vilket möjliggör effektivare uppdateringar och smidigare användarupplevelser.
const { isLoading, isError, error, data } = useQuery({
keepPreviousData: true,
queryKey: ['posts', page],
queryFn: fetchPosts,
});
Egenskapen “keepPreviousData” är satt till “true”, vilket gör det möjligt att behålla tidigare data när ny information inhämtas. Variabeln “queryKey” består av en lista med nycklar, inklusive den önskade slutpunkten och det önskade sidnumret. Slutligen används funktionen “fetchPosts” som utlösare för datainsamlingen.
I en tidigare diskussion berörde vi det faktum att kroken erbjuder flera tillstånd som kan dekonstrueras på ett sätt som är analogt med dissekering av matriser och objekt. Genom att använda dessa tillstånd blir det möjligt att förbättra den övergripande användarupplevelsen genom att återge lämpliga gränssnitt under fasen för datainhämtning. Bland de tillgängliga alternativen finns “isLoading”, “isError” och flera andra.
För att kunna visa olika meddelanden beroende på den aktuella statusen för den pågående proceduren, är det nödvändigt att införliva det medföljande kodavsnittet, vilket gör det möjligt att rendera distinkta meddelanden för varje steg i den pågående processen.
if (isLoading) {
return (<h2>Loading...</h2>);
}
if (isError) {
return (<h2 className="error-message">{error.message}</h2>);
}
Sammanfattningsvis är det viktigt att tillhandahålla koden för de JavaScript-komponenter som kommer att visas i webbläsaren. Detta säkerställer inte bara korrekt rendering av komponenterna, utan möjliggör också kommunikation mellan klient- och serverapplikationerna genom användning av händelsehanterare och datahämtningsmetoder.
Med hjälp av funktionaliteten i useQuery-kroken samlas en samling hämtade inlägg effektivt inom ramen för datavariabeln. Denna aggregering underlättar underhållet av applikationens interna tillstånd. Därefter kan en traversal utföras på ackumuleringen av inlägg som lagras i nämnda variabel, vilket kulminerar i deras visuella visning på webbläsargränssnittet.
För att ge användarna möjlighet att navigera genom ytterligare paginerade data är det nödvändigt att införliva två navigeringsknappar, märkta “Föregående” och “Nästa”. Dessa knappar gör det möjligt för användarna att komma åt ytterligare sidor med innehåll när de presenteras i ett paginerat format.
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>
);
Slutligen startar du utvecklingsservern.
npm run dev
Vänligen navigera till " http://localhost:3000/Pagination " på din webbläsare för ytterligare instruktioner.
Genom att införliva Pagination-komponenten i applikationens katalogstruktur känner Next.js igen den som en anvisad rutt, vilket möjliggör sömlös navigering till motsvarande webbsida via dess specifika URL.
Infinite Scrolling med useInfiniteQuery Hook
Infinite Scrolling skapar ett till synes oavbrutet användargränssnitt genom att dynamiskt ladda ytterligare innehåll när användaren fortsätter att scrolla. Ett exempel på detta är funktionaliteten hos YouTube, där nya videoposter hämtas och presenteras utan något observerbart avbrott när man går ner genom webbsidan.
Med hjälp av kroken useInfiniteQuery
kan man underlätta oändlig scrollning genom hämtning av data från en fjärrserver, där efterföljande sidor automatiskt begärs och återges när användaren successivt går nedåt.
För att kunna integrera infinite scrolling-funktionen i din applikation måste du skapa en ny JavaScript-fil med namnet “InfiniteScroll/page.js” och placera den i katalogen “src/app”. Därefter måste du importera olika moduler, inklusive React-komponenter och state management-bibliotek, som underlättar en sömlös integrering av denna funktion med din befintliga kodbas.
"use client"
import React, { useRef, useEffect, useState } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import './page.styles.css';
Därefter ska vi utveckla en React-funktionskomponent och inom den skapa en funktion som hämtar innehållet i inläggen på ett sätt som är analogt med den metod som används för paginering.
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
}
Till skillnad från pagineringsimplementeringen, som inte introducerar någon fördröjning vid hämtning av data, innehåller denna kod en avsiktlig paus på cirka två sekunder innan ny information hämtas. Denna avsiktliga fördröjning är utformad för att ge användarna gott om tid att scrolla och utforska den aktuella datauppsättningen, vilket i slutändan resulterar i automatisk hämtning av en uppdaterad uppsättning data när de fortsätter sin surfupplevelse.
Vid initiering upprättar den anpassade kroken useInfiniteQuery
en anslutning till servern och hämtar den första uppsättningen data vid komponentens rendering. När användaren sedan bläddrar igenom innehållet hämtar kroken självständigt efterföljande informationsmängder och införlivar dem i komponenten utan att någon ytterligare inmatning krävs.
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) : [];
Variabeln inlägg
fungerar som en ackumulator för insamling av alla inlägg från olika sidor och plattar till dem till en enhetlig matris. På så sätt förenklas processen med att iterera igenom och rendera varje inlägg individuellt.
Man kan implementera en funktion som använder Intersection Observer API för att fastställa när specifika element kommer inom gränserna för användarens synliga skärm genom att definiera en funktion. Detta gör det möjligt att övervaka användarens scrollbeteende och automatiskt ladda ytterligare data när de närmar sig slutet av en lista.
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]);
Avslutningsvis, införliva de JSX-komponenter som kommer att visas i webbläsaren.
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>
);
När du har slutfört dina ändringar, vänligen navigera till " http://localhost:3000/InfiniteScroll " för att observera deras funktionalitet i första hand.
TanStack Query: Mer än bara datahämtning
TanStack Query visar sin mångsidighet som ett omfattande datahanteringsbibliotek genom sin implementering av paginering och oändliga rullningsfunktioner. Dessa funktioner visar det breda spektrum av möjligheter som detta kraftfulla verktyg erbjuder.
Genom ett brett utbud av funktioner optimerar denna lösning administrationen av applikationsdata, vilket omfattar effektiv kontroll över tillstånd. I kombination med flera andra datacentrerade funktioner ökar den operativa effektiviteten och användarnöjdheten för webbaserade applikationer.