본문 바로가기
개인공부/메인프로젝트

페이지네이션 구현 (마이페이지)

by 뭉지야 2023. 5. 13.
728x90

드디어 페이지네이션 구현을 배웠다!!!!!!!!!!!

일단 내가 꾸며둔 마이페이지의 찜리스트 페이지(likepage)에서는

Table이라는 컴포넌트를 따로 만들어뒀고 , 그걸 Table컴포넌트를 가져다가 likepage안에서 사용했다.


일단 axios를 이용해서 데이터를 가져오자.

useEffect(()=>{
  axios
    .get(`${process.env.REACT_APP_API_URL}/members/favorite`, {
      headers: {
        "Content-Type": "application/json",
        Authorization: localStorage.getItem("authToken"),
        "ngrok-skip-browser-warning": "69420",  //ngrok cors 에러
      },
    })
    .then((res)=>{
    //console.log(res.data.data);
    setLikelist(res.data.data);
    })
    .catch((err)=> console.log(err));
}, []);

내가 쓸 데이터의 모습은 밑의 그림처럼 배열안에 객체가 있는 모습이다.

저걸 Likeitem으로 타입을 지정한다.

interface Likeitem {
  titleKor: string,
  price: number;
  quantity: number;
  capacity: number;
  reviewRating: number;
}
const [likelist, setLikelist] = useState<Likeitem[]>([]);

이렇게 해서 저 데이터는 likelist가 된다.

 

likelist의 타입을 지정하자.

interface TableProps {
  likelist: Likeitem[];
}

 

page가 가지고 있는 likelist를 Table로 내려줘야한다. 

const Table = ({ likelist }: TableProps) => {
  return (
    <>
      <StyledTable>
        <thead>
          <tr>
            <StyledTh>
              <input type="checkbox" />
            </StyledTh>
            <StyledTh>상품 목록</StyledTh>
            <StyledTh>수 량(개)</StyledTh>
            <StyledTh>가 격(원)</StyledTh>
          </tr>
        </thead>
        <tbody>
          {likelist.map((el: Likeitem, idx: number) => {
            return (
              <tr key={idx}>
                <StyledTd>
                  <input type="checkbox" />
                </StyledTd>
                <StyledTd>{el.titleKor}</StyledTd>
                <StyledTd>{el.quantity}</StyledTd>
                <StyledTd>{el.price}</StyledTd>
              </tr>
            );
          })}
        </tbody>
      </StyledTable>
    </>
  );
};

팀원이 만들어뒀던 pagination 컴포넌트에서

paginationProps내려주는걸 뭘 내리는지 보니까 얘네들이었다.

{ currentPage, setCurrentPage, itemsPerPage, totalData }

그럼 일단

likepage의 return 부분에 일단 저렇게 작성하자.

<Pagination currentPage={currentPage} setCurrentPage={setCurrentPage} 
itemsPerPage={itemsPerPage} totalData={totalData} />

 

이제 페이지네이션 기능에 대해서 생각해보자.

const [totalLength, setTotalLength] = useState<number>(0);
const [currentPage, setCurrentPage} = useState<number>(1);

//totalLength: 전체 길이
//currentPage: 첫번째페이지
//나오는 총 페이지수
const totalPages = Math.ceil(totalLength / 5);

//한페이지에 나오는 데이터를 쪼개는거다.
const paginationData = likelist.slice(5*(currentPage - 1), 5*currentPage);

이제 return 부분에서 정리하면 된다.

 

먼저 Table에 slice로 나눈 데이터를 표시한다.

<Table likelist={paginationData}></Table>

그리고 아까 위에서 적혀있던 코드를 정리하자.

<Pagination currentPage={currentPage} setCurrentPage={setCurrentPage} 
itemsPerPage={itemsPerPage} totalData={totalData} />

위에서 저렇게 적혀있던 코드를 수정하자.

<Pagination currentPage={currentPage} setCurrentPage={setCurrentPage} 
itemsPerPage={5} totalData={15} />

처음 뜨는 페이지가 1이니까 currentPage를 1로

각페이지당 5개씩 뜨게 할거니까 itemsPerPage를 5

전체 데이터 수가 15니까 totalData를 15로


//likepage.tsx

//마이페이지의 첫화면, 찜리스트 페이지

//grid 활용하는거 생각해보기!!!!

import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import axios from "axios";
import { ButtonDark } from "../../components/Common/Button";
import Pagination from "../../components/AlcoholPage/Pagination";

//components
interface Likeitem {
  titleKor: string;
  price: number;
  quantity: number;
  capacity: number;
  reviewRating: number;
}

//page에서 table로 내린애
interface TableProps {
  likelist: Likeitem[];
}

const TotalStyled = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #f7f7f7;
`;

const LikepageContainer = styled.div`
  /* border: 5px solid blue; */
  width: 100vw;
  height: 100vh;
  max-width: 1250px;
  margin-top: 150px; //호버됬을때가 150이래서 일단 150으로 설정함.
  display: flex;
  flex-direction: column;
`;

//누구누구님 등급써있는부분
const LikepageHeadStyled = styled.div`
  /* border: 3px solid black; */
  flex-grow: 3;
  font-size: 18px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background-color: #dedede;
  > p {
    margin-left: 10px;
    margin-bottom: 10px;
    color: #181818;
    font-weight: 600;
  }
`;

//찜리스트 나오는 부분
const LikepageMainStyled = styled.div`
  /* border: 3px solid red; */
  flex-grow: 7;
  > * {
    margin-bottom: 30px;
    font-size: 18px;
    /* margin-left: 60px; */
  }
  > p {
    margin-top: 30px;
    font-weight: 600;
    margin-left: 60px;
  }
`;
//선택상품 장바구니, 삭제 버튼
const LikeBtnStyled = styled.div`
  margin-top: 20px;
  margin-right: 37px;
  display: flex;
  flex-direction: row;
  float: right;
  gap: 37px;
  height: 52px;
  border-radius: 7px;
  > button {
    background-color: #222222;
    color: whitesmoke;
  }
`;

const StyledTable = styled.table`
  border: 1px solid black;
  font-size: 18px;
  margin-top: 200px;
  width: 1240px;
  height: 300px;
`;

const StyledTh = styled.th`
  border: 1px solid black;
  padding: 8px;
`;

const StyledTd = styled.td`
  border: 1px solid black;
  padding: 8px;
  text-align: center;
  vertical-align: middle;
`;

//맨밑 페이지네이션부분
const PigStyled = styled.div`
  border: 1px solid red;
  display: flex;
  flex-direction: row;
  justify-content: center;
`;

const Table = ({ likelist }: TableProps) => {
  return (
    <>
      <StyledTable>
        <thead>
          <tr>
            <StyledTh>
              <input type="checkbox" />
            </StyledTh>
            <StyledTh>상품 목록</StyledTh>
            <StyledTh>수 량(개)</StyledTh>
            <StyledTh>가 격(원)</StyledTh>
          </tr>
        </thead>
        <tbody>
          {likelist.map((el: Likeitem, idx: number) => {
            return (
              <tr key={idx}>
                <StyledTd>
                  <input type="checkbox" />
                </StyledTd>
                <StyledTd>{el.titleKor}</StyledTd>
                <StyledTd>{el.quantity}</StyledTd>
                <StyledTd>{el.price}</StyledTd>
              </tr>
            );
          })}
        </tbody>
      </StyledTable>
    </>
  );
};

const Likepage = () => {
  const [likelist, setLikelist] = useState<Likeitem[]>([]);
  const [totalLength, setTotalLength] = useState<number>(0);
  const [currentPage, setCurrentPage] = useState<number>(1);
  const navigate = useNavigate();

  //나오는 총 페이지수
  const totalPages = Math.ceil(totalLength / 5);
  const paginationData = likelist.slice(5 * (currentPage - 1), 5 * currentPage);
  // console.log(currentPage);

  useEffect(() => {
    axios
      .get(`${process.env.REACT_APP_API_URL}/members/favorite`, {
        headers: {
          "Content-Type": "application/json",
          Authorization: localStorage.getItem("authToken"),
          "ngrok-skip-browser-warning": "69420", // ngrok cors 에러
        },
      })

      .then((res) => {
        // console.log(res);
        // console.log(res.data.data);
        setLikelist(res.data.data);
        // console.log(res.data.data.length);
        setTotalLength(res.data.data.length);
      })
      .catch((err) => console.log(err));
  }, []);

  return (
    <>
      <TotalStyled>
        <LikepageContainer>
          <LikepageHeadStyled>
            <p>찐영이야님의 등급은 Green입니다.</p>
          </LikepageHeadStyled>
          <LikepageMainStyled>
            <p>찜리스트</p>
            <LikeBtnStyled>
              <ButtonDark width="150px" height="100%" onClick={() => navigate("/mypage/likepage")}>
                선택상품 장바구니
              </ButtonDark>
              {/* <button>선택상품 장바구니</button> */}
              {/* 아무주소나 이동하게 해둠 */}
              <ButtonDark width="150px" height="100%" onClick={() => navigate("/mypage/likepage")}>
                선택상품 삭제
              </ButtonDark>
            </LikeBtnStyled>
            <Table likelist={paginationData}></Table>
          </LikepageMainStyled>
          <PigStyled>
            <Pagination currentPage={currentPage} setCurrentPage={setCurrentPage} itemsPerPage={5} totalData={15} />
          </PigStyled>
        </LikepageContainer>
      </TotalStyled>
    </>
  );
};

export default Likepage;
//Pagination.tsx

import React from "react";
import styled from "styled-components";
import { PaginationProps } from "../../types/AlcholInterfaces";

const PaginationContainer = styled.div`
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 5rem;
`;

const StyledPaginationBtn = styled.button`
  width: 30px;
  height: 40px;
  margin: 0 4px;
  padding: 7px 10px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  cursor: pointer;
  background-color: #f8f9fa;
  color: #495057;
  font-size: 14px;
  transition: background-color 0.2s;

  &:hover {
    background-color: #e9ecef;
  }
  &:disabled {
    cursor: not-allowed;
    color: #fff;
    background: #a84448;
  }
`;

const Pagination: React.FC<PaginationProps> = ({ currentPage, setCurrentPage, itemsPerPage, totalData }) => {
  const totalPgaes = Math.ceil(totalData / itemsPerPage);

  const handleClickPage = (pageNum: number): void => {
    setCurrentPage(pageNum);
    window.scrollTo(0, 0);
  };

  // 페이지네이션 버튼들
  const PaginationBtns = () => {
    const btns = [];
    for (let i = 1; i <= totalPgaes; i++) {
      btns.push(
        <StyledPaginationBtn key={i} onClick={() => handleClickPage(i)} disabled={currentPage === i}>
          {i}
        </StyledPaginationBtn>,
      );
    }

    return btns;
  };

  return <PaginationContainer>{PaginationBtns()}</PaginationContainer>;
};

export default Pagination;
//Interfaces.ts

// 주류 리스트 데이터
export interface AlcoholListData {
  itemId: number;
  titleKor: string;
  discountRate: number;
  price: number;
  categories: string[];
  profile: string;
  reviewCount: number;
  reviewRating: number;
}

// 주류 리스트 데이터 Props
export interface AlcoholListProps {
  data: AlcoholListData[] | null;
  totalData: number;
  currentPage: number;
  setCurrentPage: React.Dispatch<React.SetStateAction<number>>;
  size: number;
}

// 주류 리스트 상세 데이터
// export interface AlcoholData {

// }

// 페이지네이션 Props
export interface PaginationProps {
  currentPage: number;
  setCurrentPage: React.Dispatch<React.SetStateAction<number>>;
  itemsPerPage: number;
  totalData: number;
}
728x90