Paging(Pagination / infinite Scroll)

Park Bumsoo·2022년 4월 2일
0

Paging

Library

antDesign의 pagination
https://ant.design/components/pagination/#header

npm의 infinite Scroll
https://www.npmjs.com/package/react-infinite-scroller

Pagination

대부분의 사이트들을 보면 목록이 있고 그 목록들을 넘겨주는 페이지 버튼이 있다.

이 버튼들에 담겨있는 기능을 Pagination 이라 부르며 거의 모든 사이트 들에서 사용되고 있는 기능이다.

라이브러리를 사용하면 편하지만 직접 코드를 작성해보았다.

==import와 gql, style 부분은 전부 다르니 생략==
-------------------------------------------------------------------
export default function PaginationPage() {
// useQuery를 통해 데이터를 받아왔고 이를 data에 할당해준부분
  const { data, refetch } = useQuery(FETCH_BOARDS);

  const [startPage, setStartPage] = useState(1);

// useQuery를 통해 데이터를 받아왔고 이를 dataBoardsCount에 할당해준부분
  const { data: dataBoardsCount } = useQuery(FETCH_BOARDS_COUNT);
// 페이지를 넘길 기준점 부분이다 Math.ceil 수학함수를 사용한 모습이다.
  const lastPage = Math.ceil(dataBoardsCount?.fetchBoardsCount / 10);

// 페이지를 누르는 부분
  const onClickPage = (event: any) => {
    refetch({ page: Number(event.target.id) });
  };

//이전 페이지 버튼
  const onClickPrevPage = (event: any) => {
    if (startPage === 1) return;
    setStartPage((prev) => prev - 10);
    refetch({ page: startPage - 1 });
  };


//다음 페이지 버튼
  const onClickNextPage = (event: any) => {
    if (startPage + 10 > lastPage) return;
    setStartPage((prev) => prev + 10);
    refetch({ page: startPage + 10 });
  };

//반환되어 실제로 보여지는 부분
  return (
    <div>
    // 이 부분은 query로 넘겨받은 data에 담겨있는 배열의 객체와 그 요소들을 map을 통해 표시한 부분이다.
    // 주제인 Pagination과는 상관없는 부분이다.
      {data?.fetchBoards.map((el: any) => (
        <MyRow key={el._id}>
          <MyColumn>
            <input type="checkbox" />
          </MyColumn>
          {/* <MyColumn>{el._id}</MyColumn> */}
          <MyColumn>{el.writer}</MyColumn>
          <MyColumn>{el.title}</MyColumn>
        </MyRow>
      ))}
      <span onClick={onClickPrevPage}>이전페이지</span>
      
      //실제 기능이 작동하는 페이지 부분
      {/* 사용하지 않는 요소는 _, 처리해준다. */}
      {new Array(10).fill(1).map((_, index) =>
        index + startPage <= lastPage ? (
          <button
            key={index + startPage}
            onClick={onClickPage}
            id={String(index + startPage)}
          >
            {``}
            {index + startPage}
          </button>
        ) : (
          <span></span>
        )
      )}
      <span onClick={onClickNextPage}>다음페이지</span>
    </div>
  );
}

길게 올렸지만 사실 핵심은

{new Array(10).fill(1).map((_, index) =>
        index + startPage <= lastPage ? (
          <button
            key={index + startPage}
            onClick={onClickPage}
            id={String(index + startPage)}
          >
            {``}
            {index + startPage}
          </button>
        ) : (
          <span></span>
        )
      )}

이 부분이 전부이다. new Array를 통해 새로운 배열을 만들어 map을 사용하였으며
index + startPage <= lastPage를 조건으로 제시해
삼항연산자를 사용하여
버튼을 띄우거나 빈공간 표시해준다.
이렇게 되면 마지막 게시글이 15번페이지에 있다면
다음 글목록으로 넘길경우 16~20 페이지는 <span></span>이 전부이며 화면상에는 나오지않게된다.

infinite Scroll

infinite Scrolld은 npn의 Library를 사용하였다.

export default function StaticRoutedPage() {
  const { data, fetchMore } = useQuery(FETCH_BOARDS);

  console.log(data);

  const onLoadMore = () => {
    if (!data) return; // 데이터가 없으면 요청하지말하라

    fetchMore({
      variables: { page: Math.ceil(data?.fetchBoards.length / 10) },
      updateQuery: (prev, { fetchMoreResult }) => {
        if (!fetchMoreResult.fetchBoards)
          return { fetchBoards: [...prev.fetchBoards] };
        return {
          fetchBoards: [...prev.fetchBoards, ...fetchMoreResult.fetchBoards],
        };
      },
    });
  };
  return (
    <div style={{ height: "700px", overflow: "auto" }}>
      <InfiniteScroll
        pageStart={0}
        loadMore={onLoadMore}
        //   loadMore={loadFunc}  //추가로 패치할것
        hasMore={true}
        //   hasMore={true || false}
        //   loader={
        //     <div className="loader" key={0}>
        //       Loading ...
        //     </div>
        //   }
        useWindow={false}
      >
        {data?.fetchBoards.map((el, index) => (
          <MyRow key={el._id}>
            <MyColumn>{el._id}</MyColumn>
            <MyColumn>{el.writer}</MyColumn>
            <MyColumn>{el.title}</MyColumn>
          </MyRow>
        ))}
      </InfiniteScroll>
    </div>
  );

핵심이 되는 부분은
return문 안쪽의 <div> 태그 부터 그 사이의 부분이다.

	<div style={{ height: "700px", overflow: "auto" }}>
      <InfiniteScroll
        pageStart={0}
        loadMore={onLoadMore}
        //   loadMore={loadFunc}  //추가로 패치할것
        hasMore={true}
        //   hasMore={true || false}
        //   loader={
        //     <div className="loader" key={0}>
        //       Loading ...
        //     </div>
        //   }
        useWindow={false}
      >
        {data?.fetchBoards.map((el, index) => (
          <MyRow key={el._id}>
            <MyColumn>{el._id}</MyColumn>
            <MyColumn>{el.writer}</MyColumn>
            <MyColumn>{el.title}</MyColumn>
          </MyRow>
        ))}
      </InfiniteScroll>
    </div>

위에선 저렇게 코드를 작성했지만 실제로는

<InfiniteScroll
        pageStart={0}
        loadMore={loadFunc} 
        hasMore={true || false}
        loader={
        	<div className="loader" key={0}>
        	Loading ...
        	</div>
         }
      >
        {data}
</InfiniteScroll>

같은형식으로 되어있다.
pageStart={0}는 시작할 페이지의 인덱스를 나타내며
loadMore={loadFunc} 은 이후 실행될함수를 나타내고
hasMore={true || false}은 스크롤에 더 가져올 정보가 있는지를 boolean 값으로 나타낸다.
loader={~} 부분은 로딩을 보여주는게 전부니 따로 설명을 생략하겠다.


위에 코드에서 추가된 부분인 두 가지는 내부의 범위를 지정해서 스크롤을 만드는 추가 요소이며

<div style={{ height: "700px", overflow: "auto" }}>
는 직접적인 범위를 나타내주며
useWindow={false}
는 스크롤을 해당 윈도우 페이지가 아닌 독립적인 스크롤을 사용하겠다 한것이다.

아래부분은 loadMore={loadFunc} {loadFunc}대신 들어갈 함수부분인 {onLoadMore} 이다.

const onLoadMore = () => {
    if (!data) return; // 데이터가 없으면 요청하지말하라

    fetchMore({
      variables: { page: Math.ceil(data?.fetchBoards.length / 10) },
      updateQuery: (prev, { fetchMoreResult }) => {
        if (!fetchMoreResult.fetchBoards)
          return { fetchBoards: [...prev.fetchBoards] };
        return {
          fetchBoards: [...prev.fetchBoards, ...fetchMoreResult.fetchBoards],
        };
      },
    });
  };

fetchMore({~ 부분이 실제로 추가 정보를 가져올 정보이며
variables는 gql 쿼리 부분을 통해서 가져와야 할 기본적인 정보들이고
updateQueryreturn전 업데이트할 정보들 이다.
(prev, { fetchMoreResult }) 라는 부분이 있는데 prev는 이전의 정보들을 뜻하며

{ fetchMoreResult }는 더 가져올 정보들의 결과값을 나타낸다.
fetchMore의 선언은 const { data, fetchMore } = useQuery(FETCH_BOARDS);
에서 useQuery를 통해 데이터와 함깨 선언하였으며
{ fetchMoreResult }FETCH_BOARDS라는 gql의 정보들의 결과인 셈이다.

마지막 return은 최종적으로 반환될 정보 이며

fetchBoards: [...prev.fetchBoards, ...fetchMoreResult.fetchBoards],

[...prev.~~, ...fetchMoreResult.~~]는 기존값들과 추가 값들을 합쳐
fetchBoards 에 담아 반환함을 의미한다.

profile
프론트엔드 주니어 개발자(React, Next.js)

0개의 댓글