วิธีการใช้การเลื่อนและการแบ่งหน้าแบบไม่สิ้นสุดด้วย Next.js และ TanStack Query
แอพส่วนใหญ่ที่คุณจะพัฒนาจะจัดการข้อมูล เมื่อโปรแกรมต่างๆ ขยายตัวอย่างต่อเนื่อง ก็อาจมีโปรแกรมจำนวนมากขึ้นเรื่อยๆ เมื่อแอปพลิเคชันล้มเหลวในการจัดการข้อมูลจำนวนมากอย่างมีประสิทธิภาพ แอปพลิเคชันเหล่านั้นก็จะทำงานได้ไม่ดี
การใช้การแบ่งหน้าและการเลื่อนแบบไม่มีที่สิ้นสุดเป็นวิธีการปฏิบัติจริงในการเพิ่มประสิทธิภาพแอปพลิเคชัน ช่วยให้การจัดการการแสดงผลข้อมูลดีขึ้นในขณะเดียวกันก็ปรับปรุงประสบการณ์ผู้ใช้โดยรวม
การแบ่งหน้าและการเลื่อนแบบไม่มีที่สิ้นสุดโดยใช้ TanStack Query
TanStack Query €”การดัดแปลง React Query€” เป็นไลบรารีการจัดการสถานะที่มีประสิทธิภาพสำหรับแอปพลิเคชัน JavaScript โดยนำเสนอโซลูชันที่มีประสิทธิภาพสำหรับการจัดการสถานะแอปพลิเคชัน ท่ามกลางฟังก์ชันอื่นๆ รวมถึงงานที่เกี่ยวข้องกับข้อมูล เช่น การแคช
การแบ่งหน้าเป็นวิธีการจัดระเบียบชุดข้อมูลขนาดใหญ่โดยแบ่งออกเป็นส่วนที่เล็กลงและนำทางได้อย่างง่ายดายผ่านการใช้ตัวควบคุมการแบ่งหน้า ในทางกลับกัน การเลื่อนแบบไม่มีที่สิ้นสุดเสนอวิธีการเรียกดูแบบปรับเปลี่ยนได้ โดยที่เมื่อผู้ใช้เลื่อนลง ข้อมูลเพิ่มเติมจะถูกโหลดและแสดงอย่างราบรื่น ดังนั้นจึงไม่จำเป็นต้องมีการนำทางโดยตรง
การแบ่งหน้าและการเลื่อนแบบไม่สิ้นสุดเป็นทั้งวิธีการจัดการและแสดงข้อมูลจำนวนมากในลักษณะที่เป็นมิตรต่อผู้ใช้ โดยแต่ละวิธีมีข้อดีและข้อเสียแตกต่างกันไป ขึ้นอยู่กับความต้องการเฉพาะของแอปพลิเคชัน
คุณสามารถค้นหาซอร์สโค้ดสำหรับโปรเจ็กต์นี้ได้ภายในพื้นที่เก็บข้อมูล GitHub ที่กำหนดไว้
การตั้งค่าโครงการ Next.js
ในการเริ่มต้นกระบวนการ ให้สร้างโปรเจ็กต์ Next.js โดยการติดตั้งเวอร์ชันล่าสุด เวอร์ชัน 13 ซึ่งใช้ไดเรกทอรี “App” เป็นรากฐาน
npx create-next-app@latest next-project --app
ในการดำเนินการต่อ คุณจะต้องติดตั้งแพ็คเกจ TanStack ภายในโปรเจ็กต์ของคุณโดยใช้ npm ซึ่งเป็นตัวจัดการแพ็คเกจที่ใช้กันอย่างแพร่หลายสำหรับแอปพลิเคชัน Node.js
npm i @tanstack/react-query
รวม TanStack Query ในแอปพลิเคชัน 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 Hook
การใช้ hook 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 ภายนอก ในกรณีนี้ ให้ใช้ JSONPlaceholder API เพื่อดึงชุดของโพสต์
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
}
สามารถกำหนด hook useQuery
ได้โดยการระบุออบเจ็กต์ที่มีคู่คีย์-ค่าหลายคู่ที่แสดงถึงพารามิเตอร์ต่างๆ ที่จำเป็นในการดึงข้อมูลจาก GraphQL API พารามิเตอร์เหล่านี้อาจรวมถึงสตริงการสืบค้น ตัวแปรสำหรับการปรับแต่งการสืบค้น ตัวเลือกสำหรับควบคุมวิธีการดึงข้อมูลและอัปเดต และลองนโยบายอีกครั้งในกรณีที่เครือข่ายเกิดข้อผิดพลาดหรือปัญหาอื่น ๆ ด้วยการจัดเตรียมพารามิเตอร์เหล่านี้ภายในบริบทของฮุก useQuery
เราเปิดใช้งานแผนผังส่วนประกอบ React เพื่อจัดการสถานะที่เกี่ยวข้องกับวงจรการสืบค้น ช่วยให้อัปเดตมีประสิทธิภาพมากขึ้นและประสบการณ์ผู้ใช้ที่ราบรื่นยิ่งขึ้น
const { isLoading, isError, error, data } = useQuery({
keepPreviousData: true,
queryKey: ['posts', page],
queryFn: fetchPosts,
});
คุณสมบัติ “keepPreviousData” ถูกตั้งค่าเป็น “true” ซึ่งช่วยให้สามารถเก็บรักษาข้อมูลก่อนหน้าเมื่อได้รับข้อมูลใหม่ ตัวแปร “queryKey” ประกอบด้วยรายการคีย์ รวมถึงจุดสิ้นสุดที่จำเป็นและหมายเลขหน้าที่ต้องการ สุดท้าย การเรียกใช้ฟังก์ชัน"fetchPosts"จะทำหน้าที่เป็นตัวกระตุ้นในการรับข้อมูล
ในการสนทนาครั้งก่อน เราได้กล่าวถึงข้อเท็จจริงที่ว่า hook มีหลายสถานะซึ่งสามารถแยกส่วนได้ในลักษณะที่คล้ายคลึงกับการแยกอาร์เรย์และอ็อบเจ็กต์ ด้วยการใช้สถานะเหล่านี้ จะเป็นไปได้ที่จะปรับปรุงประสบการณ์ผู้ใช้โดยรวมด้วยการแสดงอินเทอร์เฟซที่เหมาะสมในระหว่างขั้นตอนการดึงข้อมูล ในบรรดาตัวเลือกที่มีอยู่ ได้แก่’isLoading’,‘isError’และอื่น ๆ อีกมากมาย
เพื่อที่จะแสดงหน้าจอข้อความต่างๆ ขึ้นอยู่กับสถานะปัจจุบันของขั้นตอนที่กำลังดำเนินอยู่ จำเป็นต้องรวมข้อมูลโค้ดที่ให้มา ซึ่งช่วยให้สามารถแสดงข้อความที่แตกต่างกันสำหรับแต่ละขั้นตอนของกระบวนการที่กำลังดำเนินอยู่
if (isLoading) {
return (<h2>Loading...</h2>);
}
if (isError) {
return (<h2 className="error-message">{error.message}</h2>);
}
โดยสรุป จำเป็นต้องจัดเตรียมโค้ดสำหรับส่วนประกอบ JavaScript ที่จะแสดงบนเว็บเบราว์เซอร์ สิ่งนี้ไม่เพียงแต่รับประกันการเรนเดอร์ส่วนประกอบอย่างเหมาะสม แต่ยังช่วยให้เกิดการสื่อสารระหว่างแอปพลิเคชันฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์ผ่านการใช้เครื่องมือจัดการเหตุการณ์และวิธีการดึงข้อมูล
การใช้ฟังก์ชันการทำงานที่ได้รับจาก hook 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 ” บนเว็บเบราว์เซอร์ของคุณเพื่อดูคำแนะนำเพิ่มเติม
การรวมองค์ประกอบการแบ่งหน้าภายในโครงสร้างไดเร็กทอรีของแอปพลิเคชันจะแจ้งให้ Next.js จดจำว่าเป็นเส้นทางที่กำหนด ช่วยให้สามารถนำทางไปยังหน้าเว็บที่เกี่ยวข้องได้อย่างราบรื่นผ่าน URL เฉพาะ
การเลื่อนแบบไม่มีที่สิ้นสุดโดยใช้ useInfiniteQuery Hook
การเลื่อนแบบไม่มีที่สิ้นสุดจะสร้างอินเทอร์เฟซผู้ใช้ที่ดูเหมือนไม่สะดุดด้วยการโหลดเนื้อหาเพิ่มเติมแบบไดนามิกในขณะที่ผู้ใช้เลื่อนต่อไป ตัวอย่างที่เป็นแบบอย่างของสิ่งนี้สามารถพบได้ในฟังก์ชันการทำงานของ YouTube โดยที่รายการวิดีโอใหม่จะถูกดึงและนำเสนอได้อย่างง่ายดายโดยไม่มีการหยุดชะงักที่สังเกตได้เมื่อรายการหนึ่งลงมาผ่านหน้าเว็บ
การใช้ฮุค useInfiniteQuery
ช่วยให้สามารถเลื่อนดูข้อมูลได้อย่างไม่จำกัดผ่านการดึงข้อมูลจากเซิร์ฟเวอร์ระยะไกล โดยที่หน้าที่ตามมาจะได้รับการร้องขอและแสดงผลโดยอัตโนมัติเมื่อผู้ใช้เลื่อนลงมาเรื่อยๆ
เพื่อรวมฟังก์ชันการเลื่อนแบบไม่มีที่สิ้นสุดภายในแอปพลิเคชันของคุณ จำเป็นต้องสร้างไฟล์ JavaScript ใหม่ชื่อ “InfiniteScroll/page.js” และวางไว้ภายในไดเร็กทอรี “src/app” ต่อจากนั้น คุณจะต้องนำเข้าโมดูลต่างๆ รวมถึงส่วนประกอบ React และไลบรารีการจัดการสถานะ ซึ่งอำนวยความสะดวกในการผสานรวมคุณลักษณะนี้กับโค้ดเบสที่มีอยู่ของคุณได้อย่างราบรื่น
"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
จะสร้างการเชื่อมต่อกับเซิร์ฟเวอร์ โดยดึงชุดข้อมูลเริ่มต้นเมื่อมีการเรนเดอร์ส่วนประกอบ ต่อจากนั้น เมื่อผู้ใช้เลื่อนดูเนื้อหา hook จะจัดหาชุดข้อมูลตามมาโดยอัตโนมัติ และรวมไว้ในส่วนประกอบโดยไม่ต้องป้อนข้อมูลเพิ่มเติม
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) : [];
ตัวแปร โพสต์
ทำหน้าที่เป็นตัวสะสมสำหรับรวบรวมโพสต์ทั้งหมดจากหน้าต่างๆ และทำให้เป็นอาร์เรย์แบบรวม การทำเช่นนี้จะทำให้กระบวนการวนซ้ำและแสดงผลแต่ละโพสต์แยกกันง่ายขึ้น
หนึ่งอาจใช้ฟังก์ชันการทำงานที่ใช้ 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: มากกว่าแค่การดึงข้อมูล
TanStack Query นำเสนอความสามารถรอบด้านในฐานะไลบรารีการจัดการข้อมูลที่ครอบคลุมผ่านการใช้คุณสมบัติการแบ่งหน้าและการเลื่อนแบบไม่มีที่สิ้นสุด ฟังก์ชันการทำงานเหล่านี้แสดงให้เห็นถึงความสามารถที่หลากหลายที่นำเสนอโดยเครื่องมืออันทรงพลังนี้
โซลูชันนี้เพิ่มประสิทธิภาพการจัดการข้อมูลแอปพลิเคชัน ครอบคลุมการควบคุมสถานะอย่างมีประสิทธิภาพด้วยฟังก์ชันการทำงานที่หลากหลาย เมื่อใช้ร่วมกับการดำเนินการที่เน้นข้อมูลเป็นศูนย์กลางอื่นๆ จะช่วยเพิ่มประสิทธิภาพการดำเนินงานและความพึงพอใจของผู้ใช้แอปพลิเคชันบนเว็บ