[react-infinite-scroller] 무한스크롤 적용하기

이주희·2022년 4월 3일
1

Libraries

목록 보기
9/21

연관 내용
[페이지네이션 적용하기]

무한스크롤

  • [스프레드 연산자]와 useQuery의 fetchMore을 활용해서 이전 댓글과 추가된 댓글의 배열을 연결한다.

fetchMore 속성 설명

  • variables : 새롭게 요청할 쿼리의 입력값
  • updateQuery : 기존에 저장된 query를 업데이트 한다.
  • prev: 이전 요청까지의 데이터
  • fetchMoreResult: 새롭게 가져온 데이터
  • return의 fetchBoardComments가 새로 저장한 값으로 업데이트 된다.

라이브러리 설치

[react-infinite-scroller]

react-infinite-scroller와 react infinite scroll component 둘 다 많이 쓰이고, 사용 방법도 거의 유사하다.

1. react-infinite-scroller 설치

설치할 디렉토리의 터미널에 입력한다.
yarn add react-infinite-scroller

2. @types/react-infinite-scroller 설치

위 에러는 타입 스크립트를 설치하라는 뜻이다.
yarn add @types/react-infinite-scroller --dev 👈🏻 터미널에 입력 ㄱㄱ

3. import

사용할 파일에 import한다.
import InfiniteScroll from 'react-infinite-scroller';


사용 방법

0. 속성 설명

  • loadMore: 더 있으면 스크롤을 내릴 때 작동시킬 함수를 넣는다.
    {loadFunc} : 스크롤을 내리면 실행될 함수 -> 만들어서 넣어줘야 한다.

  • hasmore : 데이터가 더 있는지 없는지

  • loader : 스크롤이 로딩되는 동안 보여줄 내용

  • {items} : 맵으로 그려주는 데이터를 넣으면 된다.

  • useWindow={false}를 추가하면 윈도우 창 자체가 아닌 컴포넌트 안에 스크롤이 생긴다.
    컴포넌트를 감싸는 태그에 height:700px; overflow:auto 추가!

1. Query를 수정한다.

추가 요청을 위해 variables를 수정해야 하는지 확인한다.
보통 조회할 페이지를 의미하는 값으로 쿼리 요청을 보낸다.

export const FETCH_BOARD_COMMENTS = gql`
  query fetchBoardComments($boardId: ID!, $page: Int) {
    fetchBoardComments(boardId: $boardId, page: $page) {
      _id
      writer
      rating
      contents
      createdAt
    }
  }
`;

2. 스크롤을 내릴 때 실행시킬 함수를 만든다.

const onLoadMore = () => {
	// 데이터가 없으면 실행하지 않는다. (처음에 데이터가 undefined일 때 무한 스크롤이 실행 되는 것을 방지하기 위함)
	if (data === undefined) return; 

    void fetchMore({
      variables: {
        // 다음 페이지(불러올 페이지) : 기존에 받아온 data에서  길이를 가져와서 활용한다.
        page: Math.ceil((data?.fetchBoards.length ?? 10) / 10) + 1
      }, 

      // useQuery로 받아온 data를 update한다.
      updateQuery: (prev, { fetchMoreResult }) => {
        // prev: 기존의 data
        // {fetchMoreResult} : 추가로 요청해서 받아온 내용

        // 새로 조회해온 값이 없으면 기존 것을 그대로 업데이트한다.
        if (fetchMoreResult.fetchBoards === undefined)
          return { fetchBoardComments: [...prev.fetchBoardComments] };

        // 가져온 내용으로 return (update한다.)
        return {
          // 기존의 것과 추가로 받은 것을 합친다.
          fetchBoardComments: [
            ...prev.fetchBoardComments,
            ...fetchMoreResult?.fetchBoardComments,
          ], 
        };
      },
    });
  };

3. 무한스크롤을 적용시킬 부분을 <InfiniteScroll>로 감싼다.

  • hasmore = {true}로 변경
  • 위에서 만든 함수를 loadMore에 바인딩
<S.CListWrapper
        commentsLength={props?.data ? props.data.fetchBoardComments.length : 0}
      >
        <InfiniteScroll
          pageStart={0}
          loadMore={props.onLoadMore}
          hasMore={true}
          useWindow={false}
        >
          {props.data?.fetchBoardComments.map((el: any, index: number) => (
            <CommentsItemUI
              key={el._id}
              el={el}
              data={props.data}
              onToggleModal={props.onToggleModal}
              onClickDelete={props.onClickDelete}
              isOpen={props.isOpen}
              onChangePassword={props.onChangePassword}
              index={index}
            />
          )) ?? <div></div>}
        </InfiniteScroll>
      </S.CListWrapper>

🚨 ERROR
Warning: Failed prop type: The prop children is marked as required in InfiniteScroll, but its value is undefined.

화면에 댓글은 잘 나오는데 위와 같은 콘솔 에러가 뜰 경우
👉🏻 데이터가 없을 경우 빈 태그를 보여주도록 처리하면 해결됨!


전체 코드

import { useQuery, gql } from "@apollo/client";
import {
  IQuery,
  IQueryFetchBoardsArgs,
} from "../../../src/commons/types/generated/types";
import InfiniteScroll from "react-infinite-scroller";

const FETCH_BOARDS = gql`
  query fetchBoards($page: Int) {
    fetchBoards(page: $page) {
      _id
      writer
      title
      contents
    }
  }
`;

export default function StaticRoutingMovedPage(): JSX.Element {
  const { data, fetchMore } = useQuery<
    Pick<IQuery, "fetchBoards">,
    IQueryFetchBoardsArgs
  >(FETCH_BOARDS);

  const onLoadMore = (): void => {
    if (data === undefined) return;

    void fetchMore({
      variables: { page: Math.ceil((data?.fetchBoards.length ?? 10) / 10) + 1 },
      updateQuery: (prev, { fetchMoreResult }) => {
        if (fetchMoreResult.fetchBoards === undefined)
          return { fetchBoards: [...prev.fetchBoards] };
        return {
          fetchBoards: [...prev.fetchBoards, ...fetchMoreResult.fetchBoards],
        };
      },
    });
  };
  return (
    <div>
      <InfiniteScroll pageStart={0} loadMore={onLoadMore} hasMore={true}>
        {data?.fetchBoards.map((el) => (
          <div key={el._id}>
            <span>{el.title}</span>
            <span>{el.writer}</span>
          </div>
        )) ?? <div></div>}
      </InfiniteScroll>
    </div>
  );
}
profile
🍓e-juhee.tistory.com 👈🏻 이사중

0개의 댓글