[React] 학습 - day7

jiseong·2021년 9월 22일
0

T I Learned

목록 보기
79/291
post-custom-banner

Can't perform a React state update on an unmounted component

  function myThrottle(callback, wait = 300) {
    let timer;

    return () => {
      if (!timer) {
        timer = setTimeout(() => {
          timer = null;
          callback();
        }, wait);
      }
    };
  }

  const onScroll = e => {
    const value = window.scrollY;
    setIsScroll(value > 50);
    setIsHeaderClick(false);
  };

 useEffect(() => {
    window.addEventListener('scroll', myThrottle(onScroll));
    return () => {
      window.removeEventListener('scroll', myThrottle(onScroll));
    };
  }, []);

분명 언마운트 될 때 removeEventListener로 cleanup을 시켜도 myThrottle로 인해 다른 페이지에서도 스크롤 이벤트가 계속 동작되어 그 안에서 setState로 인해 업데이트가 되면서 생기는 오류였다.

첫번째 방식

처음에는 setTimeout으로 인한 오류라 생각되어 timer 변수를 전역으로 빼내고 언마운트시 clearTimeout(timer)을 이용했지만 여전히 오류가 발생했다.

let timer;
function myThrottle(callback, wait = 300) {
  return () => {
    if (!timer) {
      timer = setTimeout(() => {
        timer = null;
        callback();
      }, wait);
    }
  };
}

useEffect(() => {
  window.addEventListener('scroll', myThrottle(onScroll));
  return () => {
    clearTimout(timer);
    window.removeEventListener('scroll', myThrottle(onScroll));
  };
}, []);

두번째 방식

고민해보니 클로저로 인해 addEventListener에서 생성된 실행컨텍스트가 아직 남아있었고 removeEventListener에서는 엉뚱한 실행 컨텍스트를 제거해서 생기는 오류 같았다.

이를 고치기 위해 const throttle = myThrottle(onScroll, 300);로 변수에 할당하여 언마운트 시 해당 실행 컨텍스트를 없애주는 방식으로 변경하여 에러가 뜨지 않는 것을 확인 할 수 있었다.

function myThrottle(callback, wait = 300) {
  let timer;

  return () => {
    if (!timer) {
      timer = setTimeout(() => {
        timer = null;
        callback();
      }, wait);
    }
  };
}

const onScroll = e => {
  console.log('hi');
  const value = window.scrollY;
  setIsScroll(value > 50);
  setIsHeaderClick(false);
};

useEffect(() => {
  const throttle = myThrottle(onScroll, 300);

  window.addEventListener('scroll', throttle);
  return () => {
    window.removeEventListener('scroll', throttle);
  };
}, []);

pagination

기존에 리스트의 전체를 보여주는 방식에서 페이지네이션을 구현하여 원하는 갯수만큼 보여주는 방식으로 변경하였다.

이를 위해서는 현재 페이지를 관리하는 currentPage state, 페이지마다 보여줄 갯수를 관리하는 listPerPage state를 이용하였다.

그리고 slice() 메서드를 이용하여 보여줄 숙소 리스트를 조절하였다.

const [currentPage, setCurrentPage] = useState(1);
const [listPerPage, setPerPage] = useState(5);

const idxOfLastList = currentPage * listPerPage;
const idxOfFirstList = idxOfLastList - listPerPage;
const currentAccomodationList = filteredAccomodationList.slice(idxOfFirstList, idxOfLastList);

const changePage = pageNum => {
  setCurrentPage(pageNum);
  window.scrollTo(0, 0);
};

PaginationContainer 컴포넌트는 페이지 번호를 생성하는 역할을 한다.
추후에 전역으로 props를 관리하게 되면 해당 컴포넌트에서 페이지관련 로직을 수행하게끔 변경할 예정이다.

function PaginationContainer({
  totalAccomodationList,
  listPerPage,
  accomodationList,
  idxOfFirstList,
  changePage,
  currentPage,
}) {
  const pageNumbers = Array.from(
    new Array(Math.ceil(totalAccomodationList.length / listPerPage)),
    (v, i) => i + 1,
  );

  return (
    <Pagination
      pageNumbers={pageNumbers}
      totalListLength={totalAccomodationList.length}
      listPerPage={listPerPage}
      accomodationList={accomodationList}
      idxOfFirstList={idxOfFirstList}
      changePage={changePage}
      currentPage={currentPage}
    />
  );
}

export default PaginationContainer;

Pagination 컴포넌트에서는 해당 Props를 받아 화면에 보여지게하였다.

<section css={paginationPaddingContainer}>
  <div css={paginationContainer}>
    <ul css={pageWrap}>
      {currentPage === firstPageNum ? (
        <li css={pageBtnDisable}>&#10094;</li>
      ) : (
        <li css={pageBtn} onClick={() => changePage(currentPage - 1)}>
          &#10094;
        </li>
      )}

      {pageNumbers.map((pageNum, idx) => (
        <PageItem
          pageNum={pageNum}
          key={idx}
          changePage={changePage}
          isCurrentPage={pageNum === currentPage}
          />
      ))}
      {currentPage === lastPageNum ? (
        <li css={pageBtnDisable}>&#10095;</li>
      ) : (
        <li css={pageBtn} onClick={() => changePage(currentPage + 1)}>
          &#10095;
        </li>
      )}
    </ul>
    <div css={totalPage}>{`${totalListLength}개의 숙소 중 ${idxOfFirstList + 1} ~ ${
      idxOfFirstList + accomodationList.length
                         }번째 숙소`}</div>
  </div>
</section>

현재는 모든 페이지번호가 보이며 페이지가 많아질 시 ...방식을 구현하지 않았기 때문에 추후에 수정해야한다.

post-custom-banner

0개의 댓글