프론트 129 - ligfting state up

규링규링규리링·2024년 9월 27일

프론트 공부하기

목록 보기
129/135

스테이트 끌어 올리기

지금까지 만든 페이지 네이션을 사용하려면
상품페이지 가면 상품페이지네이션이
마이페이지 가면 마이페이지네이션이
페이지 마다 똑같은 페이지네이션을 만들어야 하는데 비효율적임

안의 내용물은 달라도 페이지 네이션은 동일한 것
하나를 잘 만들어서 여기저기 쓸 수 있게 해보자.

자식1의 state를 count라고 했을때
자식1에서 count를 올렸을 때 자식 2에서도 보이고 싶다.
부모 자식이면 props로 넘기면 가능한데
형제끼리는 어떻게하면 좋을까.
이 때 사용하는게 state를 끌어올리는것.

count, setCount를 부모로 옮겨서 부모는 자식에게 넘길 수 있으니

양쪽에 전달하는 방식

이렇게 되면 자식 1에서 값이 바뀌면 자식 2에도 같이 바뀌게 됨.

자식이 부모의 state를 바꾸려면?

버튼은 자식에 있음

자식의 버튼을 누르면 부모의 state가 올라가도록 만들어야함.
지금껏 state는 부모에서 자식에게 주는 단방향 이라고 배웠음

부모의 setState를 props로 보내주면 가능해짐

부모에게서 받아온 setState를 바꿔주면 어쨋든 부모의 state이기 때문에
부모의 state값이 바뀌게됨
바뀐 state가 props를 통해서 다시 내려오게됨.

실습해보기

부모와 자식2개를 따로 만들어주고

  1. setCount 보내기
// 부모 코드
<Child1 count={count} setCount={setCount}></Child1>
// 자식 코드
function onClickCountUp () :void {
  props.setCount((prev : number) => prev + 1)
}
<button onClick={onClickCountUp}>카운트 올리기</button>

부모에서 자식으로 setCount를 보내고 자식에서 setCount로 함수를 만들어서 사용
2. 함수 보내기

// 부모 코드
const onClickCount =() :void => {
  setCount((prev : number) => prev + 1)
}
<Child2 count={count} onClickCount={onClickCount}></Child2>
// 자식 코드
<button onClick={props.onClickCount}>카운트 올리기</button>

부모에서 함수를 만들어서 자식으로 보내서 사용
둘 모두 동일하게 연동되어 동작함

1번을 눌러도 2번을 눌러도 같이 오름

페이지 네이션에 어떻게 쓰나

위에서 child 1이 게시글 목록이면
child 2 가 페이지 네이션으로 사용

다른 페이지에서는
child 1이 상품목록
child 2가 다시 페이지 네이션

이런식으로 사용하면 많은 페이지에서 페이지 네이션은 하나로 반복 사용이 가능함.

리팩토링 해보기

부모 페이지

// 보드 목록 전부 가져오기 
const FETCH_BOARDS = gql`
query getBoards($page: Int) {
	getBoards(page: $page){
	id
	writer
	title
	contents
	}
}
`
// 보드 갯수 가져오기
const COUNT_BOARDS = gql`
query {
	getBoardsCount
}
`
export default function PaginationRefactoringPage() {
  // 보드 목록을 data에 저장
	const { data, refetch } = useQuery<Pick<IQuery,"getBoards">,IQueryGetBoardsArgs>(FETCH_BOARDS) 

  // 보드 갯수 가져와서 dataBoardsCount에 저장
	const {data:dataBoardsCount} = useQuery<Pick<IQuery,"getBoardsCount">,String>(COUNT_BOARDS)

  // 최대 몇페이지까지 만들지 저장
	const lastPage = Math.ceil ((Number(dataBoardsCount?.getBoardsCount )?? 10) / 10 );

  	return (
      <>
        {/* 보드 리스트 */}
		<BoardList data={data}></BoardList>
		{/* 페이지 네이션 */}
		<PageNation refetch={refetch} lastPage={lastPage}></PageNation>
	  </>
			)
}

쿼리 부분만 부모페이지에 남기고, 나오는 데이터는 각각 자식에게 넘김

자식2 : 페이지 네이션

export default function PageNation(props:IPageNationProps):JSX.Element{
  // 현재 페이지 세트 계산할 startPage
  const [startPage, setStartPage] = useState(1);

  const onClickPage = (event:MouseEvent<HTMLSpanElement>): void => {
    void props.refetch({page: Number(event.currentTarget.id)})
  }

  const onclickPrevPage = ():void => {
    if(startPage === 1) return;
    setStartPage(startPage - 10)
    void props.refetch({page: startPage - 10})
  }

  const onclickNextPage = () :void => {
    if(startPage + 10 > props.lastPage) return;
    setStartPage(startPage + 10)
    void props.refetch({page: startPage + 10})
  }

  return(
    <>
    <span onClick={onclickPrevPage}>이전 페이지</span>
	  {
  		new Array(10).fill(1).map((_,index) => 
            index + startPage <= props.lastPage &&
            (<span 
            key={index + startPage} 
            id={String(index + startPage)} 
			onClick={onClickPage}>
  			  {index + startPage}
			</span>)
		)}
  	  <span onClick={onclickNextPage}>다음 페이지</span>
	</>
  )
}

부모에게서 받은 lastPage를 통해서 최대 페이지를 정하고
페이지 네이션을 만들고 현재 화면에 몇페이지를 띄우는지
props.refetch를 통해서 page값을 부모에게 보냄

부모는 받아온 page를 쿼리로 보내서 현재 보드의 목록을 data로 저장해서
자식1로 보냄

자식1 : 보드 목록

export default function BoardList(props : IBoardListProps) :JSX.Element {
  return (
    <div>
      {props.data?.getBoards?.map(el => (
        <div key={el?.id}>
          <span style={{ margin: "15px", padding: "0px" }}>{el?.title}</span>
          <span style={{ margin: "15px" }}>{el?.writer}</span>
        </div>
      ))}
    </div>
  )
}

받아온 게시글 목록을 출력함.

0개의 댓글