[react] 무한스크롤 구현

woo·2022년 3월 28일
0

기능 구현

목록 보기
4/17

IntersectionObserver 무한 스크롤을 구현하였다. 그리고 데이터를 불러오는 동안 react-loading 으로 loder을 만들었다.

🌱 무한스크롤 구현하기

처음에는 scroll event를 이용했지만, 매 scroll마다 정확도가 떨어져 무한스크롤을 구현하기위해 IntersectionObserver API를 사용하였다. 참고글1, 참고글2을 바탕으로 여러번의 수정 끝에 구현하였다.

🖐IntersectionObserver

const observer = new IntersectionObserver(callback, options); //(콜백함수, 옵션)

실제 코드의 주석을 통해 같이 살펴보겠다.

function InfinityScroll(){
  const target = useRef();
  // 데이터를 불러올때 로딩바 돌아가기
  const [isLoaded, setIsLoaded] = useState(false);
  // 아이템을 담을 list
  const [itemLists, setItemLists] = useState([]);
  // 아이템을 3개씩 불러오고 3개가 한 묶음으로 페이지가 됨
  const [page, setPage] = useState(0);
  // 마지막 데이터 페이지가 불러오면 로딩을 멈춤
  const [datatFinish, setDatatFinish] = useState(false);

  const fetchItems = async () => {
    if (datatFinish === false) {
      setPage((page) => page + 1);
    }
  };

  const onIntersect = async ([entry], observer) => {
    if (entry.isIntersecting && !isLoaded) {
      observer.unobserve(entry.target); // 관찰요소 리셋
      await fetchItems(); // 데이터를 불러옴
      observer.observe(entry.target); // 다시 관찰요소 지정
    }
  };

  useEffect(() => {
    let observer;
    if (target.current) {
      observer = new IntersectionObserver(onIntersect, {
        threshold: 0.4, // 관찰요소와 40%만큼 겹쳤을 때 onIntersect을 수행
      });
      observer.observe(target.current);
    }
    return () => observer && observer.disconnect();
  }, [onIntersect]);

  useEffect(async () => {
    setIsLoaded(true);
    try {
      // 서버 api호출
      setItemLists([...itemLists, ....content]);
      const totalElement = totalElements;
      if ((page + 1) * 3 >= totalElement) {
        setDatatFinish(true);
      }
    } catch(e) {
      console.log(e);
      setDatatFinish(true);
    } finally {
      setIsLoaded(false);
    }
  }, [page]);
  return (
    <div ref={target} className="Target-Element"> // 관찰타겟
     {isLoaded && <Loader />}
    </div>
  )
}

Loader component

import ReactLoading from "react-loading";
const LoaderWrap = styled.div`
  width: 100%;
  height: 80%;
  display: flex;
  justify-content: center;
  text-align: center;
  align-items: center;
`;

const Loader = () => {
  return (
    <LoaderWrap>
      <ReactLoading type="spin" color="#6667AB" />
    </LoaderWrap>
  );
};
profile
🌱 매일 성장하는 개발자

0개의 댓글