IntersectionObserver 무한 스크롤을 구현하였다. 그리고 데이터를 불러오는 동안 react-loading 으로 loder을 만들었다.
처음에는 scroll event를 이용했지만, 매 scroll마다 정확도가 떨어져 무한스크롤을 구현하기위해 IntersectionObserver API를 사용하였다. 참고글1, 참고글2을 바탕으로 여러번의 수정 끝에 구현하였다.
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>
);
};