[TIL 0330] 페이지네이션_일반방식

zitto·2023년 4월 1일
0

TIL

목록 보기
20/77
post-thumbnail

🏷️ 페이지네이션

: 페이지 번호를 클릭해서 이동하는 방식의 페이지 처리 방법

✔️ 일반적인 방식



[실습-1]

import { useQuery, gql } from "@apollo/client";
import type { MouseEvent } from "react";
import type {
   IQuery,
   IQueryFetchBoardsArgs,
} from "../../../src/commons/types/generated/types";
// page 인자를 사용해서 게시글 목록 불러오기
const FETCH_BOARDS = gql`
  query fetchBoards($page: Int) {
	fetchBoards(page: $page) {
       _id
       writer
       title
       contents
     }
   }
 `;
export default function StaticRoutingPage(): JSX.Element {
// refetch하여 페이지 클릭 시 게시글 목록 데이터 다시 불러오기 
   const { data, refetch } = useQuery<
     Pick<IQuery, "fetchBoards">,IQueryFetchBoardsArgs>(FETCH_BOARDS);
   console.log(data);
//페이지번호 클릭시마다 data가 다시불려오게 되고 이 데이터로 페이지를 다시 그려줌
   const onClickPage = (event: MouseEvent<HTMLSpanElement>): void => {
// 위에서 가져온  refetch 사용하기
// void ?? 함수 안에서 기다릴건지 안기다릴건지
// 리패치 할 때까지 기다렸다가 실행하고 싶으면 await
// async promise <void>기다려야 되는 void
     void refetch({ page: Number(event.currentTarget.id) }); 
   }; //id는 string타입이므로 number로 감싸주기
   return (
     <div>
       {data?.fetchBoards.map((el) => (
         <div key={el._id}>
           <span style={{ margin: "10px" }}>{el.title}</span>
           <span style={{ margin: "10px" }}>{el.writer}</span>
         </div>
       ))}
       //배열과 map을 이용해 페이지네이션 뿌리기
       {new Array(10).fill("안녕").map((_, index) => (
         <span key={index + 1} id={String(index + 1)} onClick={onClickPage}>
           {index + 1}
         </span>
       ))}
     </div>
   );
 }


[실습-2] 페이지네이션 next / prev 구현

  • index 활용하기
export default function StaticRoutingPage(): JSX.Element {
  const [startPage, setStartPage] = useState(1); 
// 각 페이지의 시작페이지: 1, 11, 21, ...
  const { data, refetch } = useQuery<
    Pick<IQuery, "fetchBoards">,IQueryFetchBoardsArgs>(FETCH_BOARDS);
  console.log(data);
//================================> 클릭한 페이지의 게시글 목록가져옴
  const onClickPage = (event: MouseEvent<HTMLSpanElement>): void => {
    void refetch({ page: Number(event.currentTarget.id) });
  };
//================================> 이전 페이지 클릭시 실행할 함수
  const onClickPrevPage = (): void => {
    setStartPage(startPage - 10);
    void refetch({ page: startPage - 10 });
  };
//================================> 다음 페이지 클릭시 실행할 함수
  const onClickNextPage = (): void => {
    setStartPage(startPage + 10);
    void refetch({ page: startPage + 10 });
  };
//jsx
  return (
    <div>
      {data?.fetchBoards.map((el) => (
        <div key={el._id}>
          <span style={{ margin: "10px" }}>{el.title}</span>
          <span style={{ margin: "10px" }}>{el.writer}</span>
        </div>
      ))}
      <span onClick={onClickPrevPage}>이전페이지</span>
      {new Array(10).fill("안녕").map((_, index) => (
        <span
          key={index + startPage}
          id={String(index + startPage)}
          onClick={onClickPage}

          {index + startPage}
        </span>
      ))}
      <span onClick={onClickNextPage}>다음페이지</span>
    </div>
  );
}

[실습-3] lastPage 설정

import { useQuery, gql } from "@apollo/client";
import { MouseEvent, useState } from "react";
import type {
  IQuery,
  IQueryFetchBoardsArgs,
  IQueryFetchBoardsCountArgs,
} from "../../../src/commons/types/generated/types";
const FETCH_BOARDS = gql`
  query fetchBoards($page: Int) {
    fetchBoards(page: $page) {
      _id
      writer
      title
      contents
    }
  }
`;
//DB에 등록된 게시글의 총 개수를 불러오기
const FETCH_BOARDS_COUNT = gql`
  query {
    fetchBoardsCount
  }
`;
export default function StaticRoutingPage(): JSX.Element {
  const [startPage, setStartPage] = useState(1); // 각 페이지의 시작페이지: 1, 11, 21, ...
  const { data, refetch } = useQuery<
    Pick<IQuery, "fetchBoards">,IQueryFetchBoardsArgs
(FETCH_BOARDS);
  console.log(data);
  const { data: dataBoardCount } = useQuery<Pick<IQuery, "fetchBoardsCount">,IQueryFetchBoardsCountArgs(FETCH_BOARDS_COUNT);
//ceil 메소드로 마지막 페이지의 값 구하기
//총페이지가 83이라면 10으로 나눴을 때 8.3 -> 올림처리해서 9페이지
  const lastPage = Math.ceil((dataBoardCount?.fetchBoardsCount ?? 10) / 10);
//================================> 클릭한 페이지의 게시글 목록가져옴
  const onClickPage = (event: MouseEvent<HTMLSpanElement>): void => {
    void refetch({ page: Number(event.currentTarget.id) });
  };
//================================> 이전 페이지 클릭시 실행할 함수
  const onClickPrevPage = (): void => {
    // startPage가 1이면 하단 스크립트를 실행하지 않고 종료한다.
    if (startPage === 1) return;
    setStartPage(startPage - 10);
    void refetch({ page: startPage - 10 });
  };
//================================> 다음 페이지 클릭시 실행할 함수
  const onClickNextPage = (): void => {
    //startPage + 10가 lastPage보다 클 경우 하단 스크립트를 실행하지 않고 종료한다.
    if (startPage + 10 <= lastPage) {
      setStartPage(startPage + 10);
      void refetch({ page: startPage + 10 });
    }
  };
//jsx
  return (
    <div>
      {data?.fetchBoards.map((el) => (
        <div key={el._id}>
          <span style={{ margin: "10px" }}>{el.title}</span>
          <span style={{ margin: "10px" }}>{el.writer}</span>
        </div>
      ))}
      <span onClick={onClickPrevPage}>이전페이지</span>
      {new Array(10).fill("안녕").map((_, index) =>
   // _ 언더바 : 안쓰겠다는 의미
        index + startPage <= lastPage ? (
          <span
            key={index + startPage}
            id={String(index + startPage)}
            onClick={onClickPage}

            {index + startPage}
          </span>
        ) : (
          <span>없습니다</span>
        )
      )}
      <span onClick={onClickNextPage}>다음페이지</span>
    </div>
  );
}

[리팩토링]

  • 기존코드
const onClickPage1 = (event: MouseEvent<HTMLSpanElement>): void => {
    void refetch({ page: Number(event.currentTarget.id) });
};
const onClickPage2 = (event: MouseEvent<HTMLSpanElement>): void => {
    void refetch({ page: Number(event.currentTarget.id) });
};
const onClickPage3 = (event: MouseEvent<HTMLSpanElement>): void => {
    void refetch({ page: Number(event.currentTarget.id) });
};
return(
 	<span id="1" onClick={onClickPage1}>1</span>
    <span id="2" onClick={onClickPage2}>2</span>
    <span id="3" onClick={onClickPage3}>3</span>
)
  • 변경된코드
const onClickPage = (event: MouseEvent<HTMLSpanElement>): void => {
    void refetch({ page: Number(event.currentTarget.id) });
return (
    <div>
      {data?.fetchBoards.map((el) => (
        <div key={el._id}>
          <span style={{ margin: "10px" }}>{el.title}</span>
          <span style={{ margin: "10px" }}>{el.writer}</span>
        </div>
      ))}
      {new Array(10).fill("안녕").map((_, index) => {
        <span key={index + 1} id={String(index + 1)} onClick={onClickPage}>
          {index + 1}
        </span>;
      })}
};
  1. 중복되는 부분 찾기
  2. 해당 태그에 id 주기
  3. event.target.id로 router.push 경로 이동하기
    이때는 target이 태그일수도 아닐수도 있음
  4. 해결방법 : currentTarget.id 로 바꿈.
  5. onClick 이름을 하나로 통합시킨다.
  6. props도 하나만 남김
  7. 통합시킨 onClick명으로 presenter에서 props.onClick명으로 바꿔준다.
  8. 배열을 만들어서 id를 객체형태로 넣어놓는다.
  9. 반복이 안되는 것들만 map으로 반복문을 돌린다.

[lifting state-up]

state를 끌어올리다!
핵심! 두 형제가 서로에게 state를 공유하고 싶은데
어떻게 공유할 건지? state 를 부모로 끌어올림!
부모의 state를 props로 넘겨줄 수 있게 된다!
즉, 두 형제들이 같은 state를 공유할 수 있게 됨!

어떤 데이터를 공유하게 되냐? 마지막페이지가 공유되야 함!
그러기 위해선 전체갯수가 필요함!
1. 전체갯수를 props로 받아와야 한다.
2. refetch함수가 공유되어야 한다. refetch를 props로 받아와야 한다.

목록과 데이터를 공유해야 하기떄문에!
목록의 개수에 따라 페이지네이션의 마지막페이지가 결정된다.

  • index.tsx
import { useState } from "react";
import Child1 from "../../../src/components/units/15-lifting-state-up/child1";
import Child2 from "../../../src/components/units/15-lifting-state-up/child2";
export default function CounterStatePage(): JSX.Element {
  const [count, setCount] = useState(0);
  function onClickCountUp(): void {
    setCount(count + 1);
  }
  return (
    <div>
      <Child1 count={count} onClickCountUp={onClickCountUp} />
      <div>=============================</div>
      <Child2 count={count} onClickCountUp={onClickCountUp} />
    </div>
  );
}
  • child1.tsx
export default function Child1(props: any): JSX.Element {
  const onClickCountUp = (): void => {
    props.setCount((prev: number) => prev + 1);
  };
  return (
    <div>
      <div>자식1의 카운트:{props.count}</div>
      <button onClick={onClickCountUp}>카운트 올리기!</button>
    </div>
  );
}
  • child2.tsx
export default function Child2(props: any): JSX.Element {
  return (
    <div>
      <div>자식2의 카운트:{props.count}</div>
      <button onClick={props.onClickCountUp}>카운트 올리기!</button>
    </div>
  );
}

graphql 주소바꾸고 typescript 재다운로드 해야함 -> yarn generate

어떻게 하면 조립을 잘 할 수 있을까!!!! 생각하자!!!!

profile
JUST DO WHATEVER

0개의 댓글