Space
article thumbnail
반응형

Before you learn

🔗 [NextJS] Sever Action

🔗 [React] useState


[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;
}
반응형
profile

Space

@Space_zero

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!