연관 내용
[무한 스크롤 적용하기]
page를 input값으로 넣어야 다음 페이지가 조회된다.
(안 넣으면 같은 페이지만 계속 불러옴..)
query fetchBoards($page: Int, $search: String) {
fetchBoards(page: $page, search: $search) {
_id
writer
title
likeCount
images
createdAt
}
}
`;
const { data, refetch } = useQuery(FETCH_BOARDS);
기존에 쓰던 refetchQueries는 mutation 이후에 작성했다.
지금 사용하려는 useQuery의 refetch는 data 옆에 refetch를 작성해서 사용한다.
data는 state와 마찬가지로, 값이 바뀌면 화면이 리렌더 된다.
이 원리를 토대로 refetch를 해서 data가 바뀌면 화면이 다시 그려지게 된다.
참고: 이전에 쓰던 refetchQueries👇🏻
const onClickLike = async () => { try { likeBoard({ variables: { boardId: String(router.query.boardId) }, refetchQueries: [ { query: FETCH_BOARD, variables: { boardId: router.query.boardId } }, ], }); } catch (error) { if (error instanceof Error) console.log(error.message); } };
refetch(Number{page: e.target.id})
refetch를 사용할 때는 안에 객체 형태로 variables를 넣어주면 된다.
전체 data 수를 구해서 (백엔드에서 api를 통해 제공한다.)
한 화면에 보여줄 data 수로 나눈 값을 마지막 페이지 번호로 저장한다. --> pagination으로 보냄
startPage는 lastPage보다 같거나 작다.
startPage
를 만들고 초기값으로 1을 넣는다.한 페이지에 보여줄 data의 수를 길이로 갖는 배열을 만들고 내용을 (아무거나) 채운다.
(함수를 실행시키는 횟수를 위한 길이만 사용할 거라서 내용은 뭐든 상관 없다.)
map의 index를 사용해서 1씩 증가하는 페이지 번호를 만든다.
startPage + index
가 lastPage
를 넘어가면 data가 없는데도 페이지 번호가 생성되기 때문에,
map을 돌리기 전에 filter로 걸러준다.
page 번호를 누르면 3에서 만든 onClickPage
함수가 실행되고,
id에 넣어둔 startPage+index
로 refetch가 이루어진다.
이전 버튼을 누르면 startPage-10
,
다음 버튼을 누르면 startPage+10
으로 startPage
를 변경한다.
변경할 startPage
로 refetch를 실행한다.
최초 페이지와 마지막 페이지일 때는 버튼이 눌리지 않도록 if문을 작성해서 return시킨다.
(setIsPrev/NextActive와 setCurrent는 현재 page에 따라 style을 적용하기 위해 추가했다.)
Boards.container.tsx
/* FETCH_BOARDS */
const { data, refetch } = useQuery(FETCH_BOARDS); // data는 state와 동일한 역할을 한다. 값이 바뀌면 리렌더된다.
...
/* Pagination에 쓸 데이터 */
const { data: dataBoardsCount } = useQuery(FETCH_BOARDS_COUNT, {
variables: { search },
});
const lastPage = Math.ceil(dataBoardsCount?.fetchBoardsCount / 10);
const [current, setCurrent] = useState<number>(1); // 게시글 번호와 현재 페이지 표시 style을 줄 때 사용
...
return (
<BoardsUI
lastPage={lastPage}
current={current}
setCurrent={setCurrent}
...
/>
);
}
Boards.presenter.tsx
{/* 게시글 번호 */}
<S.TD style={{ width: "100px" }}>
{props.current * 10 + index - 9}
</S.TD>
...
<Pagination
refetch={props.refetch}
lastPage={props.lastPage}
current={props.current}
setCurrent={props.setCurrent}
/>
Pagination.tsx
import { useState } from "react";
import { IPaginationProps } from "../../units/board/list/Boards.types";
import * as S from "./Pagination.style";
export default function Pagination(props: IPaginationProps) {
const [startPage, setStartPage] = useState(1);
const [isPrevActive, setIsPrevActive] = useState(false);
const [isNextActive, setIsNextActive] = useState(true);
const onClickPage = (e: any) => {
// Query 확인하자! (variables에 page 추가)
props.refetch({ page: Number(e.target.id) });
props.setCurrent(Number(e.target.id));
console.log("onClickPage");
};
const onClickPrevPage = () => {
if (startPage === 1) {
// 첫 페이지 묶음이면 실행ㄴㄴ
setIsPrevActive(false);
return;
}
if (startPage === 11) {
setIsPrevActive(false);
}
setIsNextActive(true);
setStartPage((prev) => prev - 10); // 이전 페이지 묶음으로 이동
props.refetch({ page: startPage - 1 }); // prev 버튼 눌렀을 때 이전 10개의 페이지 중 가장 마지막이 열리도록 변경
props.setCurrent(startPage - 1);
};
const onClickNextPage = () => {
if (props.lastPage < startPage + 10) {
// 마지막 페이지 묶음이면 실행ㄴㄴ
setIsNextActive(false);
return;
}
if (props.lastPage < startPage + 20) {
setIsNextActive(false);
}
setIsPrevActive(true);
setStartPage((prev) => prev + 10); // 다음 페이지 묶음으로 이동
props.refetch({ page: startPage + 10 });
props.setCurrent(startPage + 10);
};
return (
<S.Wrapper>
<S.Button
disabled={!isPrevActive}
onClick={onClickPrevPage}
isActive={isPrevActive}
>
<
</S.Button>
<S.PageNumberWrapper>
{new Array(10) // 한 페이지에 보여줄 data 수
.fill(1) // 아무거나 채움
// 마지막 페이지 묶음에서 데이터가 없는 번호는 노출하지 않도록 filter
.filter((_, index) => index + startPage <= props.lastPage) // el을 사용하지 않아서 _로 씀
.map((_, index) => (
<S.PageNumber
onClick={onClickPage}
id={String(index + startPage)}
key={index + startPage}
isCurrent={props.current === index + startPage}
>
{index + startPage}
</S.PageNumber>
))}
</S.PageNumberWrapper>
<S.Button
disabled={!isNextActive || props.lastPage < 11}
onClick={onClickNextPage}
isActive={isNextActive && props.lastPage >= 11}
>
>
</S.Button>
</S.Wrapper>
);
}
Pagination.style.ts
import styled from "@emotion/styled";
interface ICurrentProps {
isCurrent: boolean;
}
interface IActiveProps {
isActive: boolean;
}
export const Wrapper = styled.div`
display: flex;
flex-direction: row;
justify-content: space-around;
width: 400px;
margin-top: 30px;
`;
export const PageNumber = styled.div`
color: ${(props: ICurrentProps) => (props.isCurrent ? "#8B008B" : "gray")};
font-family: ${(props: ICurrentProps) =>
props.isCurrent ? "GmarketSansTTFMedium" : "NanumSquareL"};
cursor: pointer;
width: 37px;
font-size: 15px;
text-align: center;
:hover {
color: #8b008b;
}
`;
export const PageNumberWrapper = styled.div`
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
width: 330px;
`;
export const Button = styled.button`
color: ${(props: IActiveProps) => (props.isActive ? "black" : "white")};
cursor: ${(props: IActiveProps) => (props.isActive ? "pointer" : "")};
`;