반응형
Before you learn
[NextJS] Pagination
영상
반응형
Code
처음 로딩 시 take에 적힌 수만큼 상품 리스트를 가져옵니다.
현재는 등록된 총 상품 수가 2개 이므로,
하나씩 가져오기 위해 take : 1 를 입력하였습니다.
가져온 상품 리스트들을 props로 <ProductList> 컴포넌트에게 전달합니다.
// app/products/page.tsx
import ProductList from "@/components/product-list";
import db from "@/lib/db";
import { Prisma } from "@prisma/client";
async function getInitialProducts() {
// take에 적힌 수 만큼 상품 리스트 가져옴
const products = await db.product.findMany({
select: {
title: true,
price: true,
created_at: true,
photo: true,
id: true,
},
take: 1,
orderBy: {
created_at: "desc", // 최신 순으로 정렬함
},
});
return products;
}
// getInitialProducts의 타입 입력 (interface로 해도 됌)
export type InitialProducts = Prisma.PromiseReturnType<
typeof getInitialProducts
>;
export default async function Products() {
const initialProducts = await getInitialProducts();
return (
<div>
<ProductList initialProducts={initialProducts} />
</div>
);
}
props로 상품 리스트 초기값을 받는다.
"Load More" 버튼을 누르면 getLoadMoreClick 함수가 실행되고,
그 안의 함수인 getMoreProducts를 통해 새로운 데이터를 가져온다.
더 이상 가져올 데이터가 없으면 newProducts.length === 0 이므로
더 이상 가져올 수 없다는 표시를 유저에게 보여준다.
// components/product-list.tsx
"use client";
import { InitialProducts } from "@/app/(tabs)/products/page";
import ProductSimpleInfo from "./product-simple-info";
import { useState } from "react";
import { getMoreProducts } from "@/app/(tabs)/products/actions";
interface ProductListProps {
initialProducts: InitialProducts;
}
export default function ProductList({ initialProducts }: ProductListProps) {
const [products, setProducts] = useState(initialProducts);
const [isLoading, setIsLoading] = useState(false);
const [page, setPage] = useState(0);
const [isLastPage, setIsLastPage] = useState(false);
const onLoadMoreClick = async () => {
setIsLoading(true);
const newProducts = await getMoreProducts(page + 1);
// 마지막 상품 데이터가 아니라면 (= 더 가져올 상품 데이터가 있다면)
if (newProducts.length !== 0) {
setPage(page + 1);
setProducts([...products, ...newProducts]);
}
// 마지막 상품 데이터라면 (= 더 가져올 상품 데이터가 없다면)
else {
setIsLastPage(true);
}
setIsLoading(false);
};
return (
<div className="p-5 flex flex-col gap-5">
{products.map((product) => (
<ProductSimpleInfo key={product.id} {...product} />
))}
{isLastPage ? (
<div className="text-sm font-semibold bg-neutral-500 w-fit mx-auto px-3 py-2 rounded-md hover:opacity-90">
No more items
</div>
) : (
<button
onClick={onLoadMoreClick}
disabled={isLoading}
className="text-sm font-semibold bg-orange-500 w-fit mx-auto px-3 py-2 rounded-md hover:opacity-90 active:scale-95"
>
{isLoading ? "Loading..." : "Load more"}
</button>
)}
</div>
);
}
새로운 데이터를 가져옴
skip : 몇개를 건너뛰어서 데이터를 가져올지 정함
take : 몇개의 데이터를 가져올지 정함
아래의 코드는 하나의 데이터를 스킵한 뒤 스킵한 다음의 데이터 하나를 가져오는 코드이다.
// app/products/actions.ts
"use server";
import db from "@/lib/db";
export async function getMoreProducts(page: number) {
const products = await db.product.findMany({
select: {
title: true,
price: true,
created_at: true,
photo: true,
id: true,
},
skip: page * 1, // 만약 25개씩 보여준다면 page * 25로 하면됌
take: 1, // 만약 25개씩 보여준다면 25로 하면됌
orderBy: {
created_at: "desc",
},
});
return products;
}
반응형