14일차 / Pagination, State lifting

13일차 코드리뷰

Body에 props.children,
< Layout >안에 props.childern(페이지 컴포넌트)이 들어간다.

yarn add @types/react-slick --dev
타입스크립트가 자바스크립트로 변형돼서 실행되므로 dev에 설치한다.

브라우저에 실행되는 부분은 dependencies,
실행되지 않는 부분은 devDependencise에 설치해야한다.


npm insatll --production 하면 dependencies에 있는 것들을 설치할 수 있다.
배포할 때 필요없는 것들을 설치할 필요 없으므로 가능하면 나눠서 설치하는 것이 좋음.

메뉴 코드 리팩토링


↑ 각각 온클릭 함수 줄 때

↑ onClick을 하나의 함수로 줄이는 과정


페이지네이션

api 서버 변경

_app.tsx, codegen.yaml의 url 바꾸기
types.ts도 최신화 필요 => yarn generate

페이지 출력

export default function MapBoardPage() {
  const { data, refetch } = useQuery(FETCH_BOARDS);

  const onClickPage = (event) => {
    refetch({ page: Number(event.target.id) }); // variables 객체 , page는 숫자이므로 Number로 감싸줌
  };

  return (
    <div>
      {data?.fetchBoards.map((el) => (
        <MyRow key={el._id}>
          <MyColumn>{el.writer}</MyColumn>
          <MyColumn>{el.title}</MyColumn>
        </MyRow>
      ))}
      <span onClick={onClickPage} id="1">
        1
      </span>
      <span onClick={onClickPage} id="2">
        2
      </span>
      <span onClick={onClickPage} id="3">
        3
      </span>
    </div>
  );
}

이렇게 받아오면 페이지가 100페이지가 넘어가면 어떻게 할 것인가? 하는 문제점이 생긴다.

 {data?.fetchBoards.map((el) => (
        <MyRow key={el._id}>
          <MyColumn>{el.writer}</MyColumn>
          <MyColumn>{el.title}</MyColumn>
        </MyRow>
      ))}
      {[1, 2, 3].map((el) => (
        <span key={el} onClick={onClickPage} id={String(el)}>
          {el}
        </span>
      ))}

map을 활용하는 방법.
id에는 Number를 받아올 수 없고 el이 숫자값으로 입력되어있으므로 String으로 감싸준다.

index를 활용하는 방법.


안쓰는 것들은 언더바 처리를 한다. (관례)

이전페이지, 다음페이지

1페이지를 기준으로 잡고, 다음페이지 버튼을 클릭하면 1이라는 숫자를 더하기 10 해준다.
다음페이지를 누르면 index+11 이 나온다.
이전페이지는 index-10 해준다.

먼저 state를 잡아준다. (초기값 1)

  const [startPage, setStartPage] = useState(1)

index에 StartPage를 더해준다.

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

prev(이전 값)를 활용해 다음페이지는 +10, 이전페이지는 -10

  const onClickNextPage = () => {
    setStartPage((prev) => prev + 10);
  };

  const onClickPrevPage = () => {
    setStartPage((prev) => prev - 10);
  };

이렇게 했을 때 마지막 페이지보다 더 많은 페이지가 나오고,
마이너스 페이지가 나오게 된다.
어떻게 해결해야할까?

스타트 페이지가 1일때 이전페이지를 클릭하면 작동하지 않게 하면 된다.

  const onClickPrevPage = () => {
    if (startPage === 1) return;
    setStartPage((prev) => prev - 10);
  };

마지막 페이지가 몇페이지인지 계산해야함.
스타트페이지는 마지막 페이지까지만 나와야한다.
startPage <= lastPage
라스트 페이지는 어떻게 계산해야 할까?
게시글이 총 12개가 있으면 1페이지에 10개, 2페이지에 2개 나타난다.
총 게시글이 2페이지까지 있는 것.
12 -> 2page
35 -> 4page
앞 숫자보다 +1페이지가 된다.
12/10 -> 1.2
35/10 -> 3.5
60/10 -> 6.0
글 개수를 10으로 나눈 값에 올림을 해주면 된다.

query fetchBoardsCount를 이용해서 수를 가져오고 startPage <= lastPage 해준다!

const FETCH_BOARDS_COUNT = gql`
  query fetchBoardsCount {
    fetchBoardsCount
  }
`;
  const { data: dataBoardsCount } = useQuery(FETCH_BOARDS_COUNT)
  const lastPage = Math.ceil(dataBoardsCount?.fetchBoardsCount /10 );

이미 data라는 이름을 사용했으므로 다른 이름을 선언하고,
lastPage를 선언해준다.
처음 값은 undefined이므로 ?를 넣어야한다.

   const onClickNextPage = () => {
    if (!(startPage + 10 <= lastPage)) return;
    setStartPage((prev) => prev + 10);
  };

예를 들어 start 페이지가 171이고 last 페이지가 173이면
start 페이지가 last 페이지보다 작을 때만 실행이 되어야한다.
이것의 반대일 때는 실행이 되면 안된다.

하지만 우리는 마지막 페이지인 173까지만 출력하고 싶다.

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

삼항연산자를 사용한 방법.

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

&&연산자를 사용한 방법.
undefined를 출력하게 해도 빈값이 나오므로 가능하다.

페이지를 넘겨도 게시글이 바뀌지 않으므로 함수에 리패치를 추가해준다.

  const onClickNextPage = () => {
    if (!(startPage + 10 <= lastPage)) return;
    setStartPage((prev) => prev + 10);
    refetch({ page: startPage + 10 }); // variables 객체 , page는 숫자이므로 Number로 감싸줌
  };

  const onClickPrevPage = () => {
    if (startPage === 1) return;
    setStartPage((prev) => prev - 10);
    refetch({ page: startPage - 10 }); // variables 객체 , page는 숫자이므로 Number로 감싸줌
  };

State 끌어올리기


원래 우리가 쓰던 방식은 부모 컴포넌트에서 state를 props해서 사용하는 방식이었지만
자식들끼리 공유할 수는 없을까?

이렇게 하면 자식끼리 state를 공유할 수 있다.
이 상태에서 state를 바꾸면 자식들도 바뀐 state로 리렌더된다.
자식이 부모의 state를 바꾸고 싶으면?

부모가 가진setState를 넘겨주면 된다.

기본 틀 잡기

자식끼리 넘겨주려면 어떻게 해야하는가?
리액트는 데이터의 흐름이 단방향인것을 이용한다.

예시1

부모에 state를 넘겨주고, Child1, Child2를 리턴한다.

함수를 다 넘겨주고 props를 활용해 부모에서 자식에게로 넘긴다.

예시2

refetch가 다른곳에서 활용되고 있으므로 부모에게 끌어올려주어야한다.

Board에 refetch를 연결

Pagination에 refetch를 연결

완성

어디서든 재사용할 수 있는 Pagination 컴포넌트를 만들었다.
검색창도 같은 방식으로 만들 수 있다.
처음 만들 때는 시간이 걸릴 수 있지만, 공통 컴포넌트를 만들게 되면 개발이 훨씬 빨라진다.

좋은 웹페이지 즐겨찾기