본문 바로가기
카테고리 없음

검색과 페이지네이션, state 끌어올리기 (복습 14일차)

by 제이엠_ 2022. 4. 21.

실습, 내가 놓친 부분들

import { SliderItem, Wrapper } from "./LayoutBanner.styles";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";

export default function LayoutBannerUI() {
  const settings = {
    dots: true,
    infinite: true,
    speed: 500,
    slidesToShow: 1,
    slidesToScroll: 1,
  };

  return (
    <Wrapper>
      <Slider {...settings}>
        <div>
          <SliderItem src="/images/layout/banner01.png" />
        </div>
        <div>
          <SliderItem src="/images/layout/banner01.png" />
        </div>
        <div>
          <SliderItem src="/images/layout/banner01.png" />
        </div>
      </Slider>
    </Wrapper>
  );
}

리액트 슬릭을 임포트해서 캐러셀을 구현하는 코드, 스프레드 연산자를 활용하여 Slider 태그에 setting값을 전달해준다.

 

import { Fragment } from "react";
import { MenuItem, Wrapper } from "./LayoutNavigation.styles";
import { ILayoutNavigationUIProps } from "./LayoutNavigation.types";

const NAVIGATION_MENUS = [
  { name: "파이어베이스게시판", page: "/myfirebase" },
  { name: "라이브강아지", page: "/openapis" },
  { name: "라이브게시판", page: "/boards" },
  { name: "라이브상품", page: "/markets" },
  { name: "마이페이지", page: "/mypages" },
];

export default function LayoutNavigationUI(props: ILayoutNavigationUIProps) {
  return (
    <Wrapper>
      {NAVIGATION_MENUS.map((el) => (
        <Fragment key={el.page}>
          <MenuItem id={el.page} onClick={props.onClickMenu}>
            {el.name}
          </MenuItem>
        </Fragment>
      ))}
    </Wrapper>
  );
}

메뉴를 만들 때 하나하나 div나 span 태그로 하나하나 만드는 것보다. 전역에 NAVIGATION_MENUS를 배열로 만들어서 map으로 구현시킬 수 있다. (이렇게 배웠는데 왜 적용을 해볼 생각을 못 했을까..?) MenuItem의 id값에 el.page로 하여 onClickMenu로 페이지 이동하는 함수도 여러 개 만들 필요 없이 하나로 다 쓸 수 있는데 말이다. 처음 들었을 때 집중하지 않고 공부를 했나보다.

 

그리고 NAVIGATION_MENUS을 밖에 빼는 이유는 함수형 컴포넌트 안에 자바스크립트 영역에 작성할 경우에는 다시 그려지기 때문에 낭비가 될 수 있다.

 

Pagination

출처 : (주)뉴비즈스타트 코드캠프

페이지네이션의 종류에는 왼쪽처럼 숫자를 클릭할 때마다 새로운 리스트를 보여주는 방법이 있고 오른쪽처럼 계속 내려서 리스트를 보여주는 방법이 있다.

 

import { gql, useQuery } from "@apollo/client";

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

export default function PaginationPage() {
  const { data, refetch } = useQuery(FETCH_BOARDS, { variables: { page: 1 } });

  const onClickPage = (event) => {
    refetch({ page: Number(event.target.id) });
  };

  return (
    <div>
      <h1>페이지네이션 연습!!!</h1>
      {data?.fetchBoards?.map((el) => (
        <div key={el._id}>
          {el.title} {el.writer}
        </div>
      ))}
      {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((el) => (
        <span key={el} onClick={onClickPage} id={String(el)}>
          {` ${el} `}
        </span>
      ))}
      {/* 
        <span onClick={onClickPage} id="1">1</span>
        <span onClick={onClickPage} id="2">2</span>
        <span onClick={onClickPage} id="3">3</span> 
      */}
    </div>
  );
}

위 코드블록처럼 할 경우에는 페이지가 10이 넘어가면 하나씩 추가해줘야하는 불편함이 있다. 전체 글에 따라서 페이지가 자동으로 생겨나도록 구현하는 건 아내 코드블록을 살펴보자.

 

import { gql, useQuery } from "@apollo/client";
import { useState } from "react";

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

export default function PaginationNextPage() {
  const [startPage, setStartPage] = useState(1);
  const { data, refetch } = useQuery(FETCH_BOARDS, { variables: { page: 1 } });

  const onClickPage = (event) => {
    refetch({ page: Number(event.target.id) });
  };

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

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

  return (
    <div>
      <h1>페이지네이션 연습!!!</h1>
      {data?.fetchBoards?.map((el) => (
        <div key={el._id}>
          {el.title} {el.writer}
        </div>
      ))}
      <span onClick={onClickPrevPage}>이전페이지</span>
      {new Array(10).fill(1).map((_, index) => (
        <span
          key={index + startPage}
          onClick={onClickPage}
          id={String(index + startPage)}
        >
          {` ${index + startPage} `}
        </span>
      ))}
      <span onClick={onClickNextPage}>다음페이지</span>
    </div>
  );
}

new Array(요소의 개수)로 빈 배열을 만들어주고 fill(채울 내용)로 배열을 채워준다. 페이지 버튼을 1, 11, 21, 31로 바꿔주면 뒤에 숫자는 잘 따라올 것이다. 시작 페이지의 초기값을 1로 시작해주고 이와 관련해서 다음페이지를 누를 때는 onClickNextPage, 이전페이지를 누를 때는 onClickPrevPage 함수를 실행하도록 하여 각각 10을 더하거나 빼줘서 startPage를 변경해준다. 하지만 여기에도 문제가 있는데 이전 페이지를 누르면 -값이 나오기도 하고 게시글이 없는데 페이지의 수는 무한정 늘어난다.

 

import { gql, useQuery } from "@apollo/client";
import { useState } from "react";

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

const FETCH_BOARDS_COUNT = gql`
  query fetchBoardsCount {
    fetchBoardsCount
  }
`;

export default function PaginationLastPage() {
  const [startPage, setStartPage] = useState(1);
  const { data, refetch } = useQuery(FETCH_BOARDS, {
    variables: { page: startPage },
  });
  const { data: dataBoardsCount } = useQuery(FETCH_BOARDS_COUNT);
  const lastPage = Math.ceil(dataBoardsCount?.fetchBoardsCount / 10);

  const onClickPage = (event) => {
    refetch({ page: Number(event.target.id) });
  };

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

  const onClickNextPage = () => {
    if (startPage + 10 > lastPage) return;
    setStartPage((prev) => prev + 10);
    // refetch({ page: startPage + 10 });
  };

  return (
    <div>
      <h1>페이지네이션 연습!!!</h1>
      {data?.fetchBoards?.map((el) => (
        <div key={el._id}>
          {el.title} {el.writer}
        </div>
      ))}
      <span onClick={onClickPrevPage}>이전페이지</span>
      {new Array(10).fill(1).map(
        (_, index) =>
          index + startPage <= lastPage && (
            <span
              key={index + startPage}
              onClick={onClickPage}
              id={String(index + startPage)}
            >
              {` ${index + startPage} `}
            </span>
          )
      )}
      <span onClick={onClickNextPage}>다음페이지</span>
    </div>
  );
}

onClickPrevPage에서는 if (startPage === 1) return 하여 이전 페이지를 막아줄 수 있다. 마지막 페이지가 중요한데 이는 전체 게시글 수를 파악하여 156개의 게시글이라면 16페이지, 201개면 21페이지까지 보여줘야한다. 전체 게시글 수를 가지고 LastPage를 10으로 나누고 올림을 해주면 된다. 그리고 함수에는 if (startPage + 10 > lastPage) return; 으로 다음 페이지를 막아준다. 추가적으로 map에서 함수를 활용하여 index + <= lastPage이면 그리게 하여 마지막 페이지까지만 보이게 해준다.

 

 

State 끌어올리기

출처:(주)뉴비즈스타트 코드캠프

자식에 setState를 넘겨주고 자식 컴포넌트에서 setState를 쓰게 되면 부모 컴포넌트의 state가 변화되는 것을 state 끌어올리기라 할 수 있다. 

import Child1 from "../../src/components/units/14-lifting-state-up/Child1";
import Child2 from "../../src/components/units/14-lifting-state-up/Child2";
import { useState } from "react";

export default function LiftingStateUpPage() {
  const [count, setCount] = useState(0);

  //   const onClickCountUp = () => {
  //     setCount((prev) => prev + 1);
  //   };

  return (
    <div>
      <Child1 count={count} setCount={setCount} />
      <div>=====================================</div>
      <Child2 count={count} />
    </div>
  );
}

위 코드블록을 보면 Child1에서 setCount 함수가 실행되며 부모컴포넌트의 count의 값이 바뀌고 Child2에 count를 props로 내려준다.

 

복습 회고

코드를 효율적으로 작성할 수 있는 방법을 수업 중간중간 하신 것들이 있었는데 이를 놓치고 활용조차 못했다는 게 아쉽다. 리팩토링을 할 때 활용해봐야겠다. state 끌어올리기도 내가 사용하고 있음에도 그 개념에 대해서 정확히 알지 못했는데 이번에 다시 한 번 알게 된 복습 시간이었다.

댓글