나는 intersection-observer를 통해 무한 스크롤을 구현한 적이 있다.
블로그를 통해 알게되었고 react프로젝트에 사용도 했었는데,
사실 react에서는 react-intersection-observer를 사용하면 된다는 것을 최근에 알게되었다.
react-intersection-observer에는 useInView훅이 존재하는데, 이것의 용법 같은 것이 정리된 글을 찾기 번거로워서 직접 사용해보면서 용법을 정리한다.
react-intersection-observer npm 페이지
ref가 viewport에 보이면!inView가 true가 된다.이 원리를 활용해서 검은색 상자가 화면에 보일 때 렌더링 되도록 아래 코드를 작성해보았다.
import { useState, useEffect } from "react";
import { useInView } from "react-intersection-observer";
export default function List() {
const { ref, inView } = useInView({
threshold: 0,
});
return (
<section ref={ref} >
{inView && <div className="bg-black h-10 w-10" />}
</section>
);
}
ref가 여러 요소에 들어가면 모든 요소가 보일 때, inView가 true가 된다.
//...생략
return (
<>
<section ref={ref} className="h-screen">
{inView && <div className="bg-black h-10 w-10" />}
</section>
<section ref={ref} className="h-screen">
{inView && <div className="bg-black h-10 w-10" />}
</section>
</>
);
}
이 원리를 활용해서 검은색 상자가 화면 맨 위와 아래에서 나타나도록 만들었다.
useEffect훅을 사용해서 감시하는 노드가 화면에 보일때 할 행동을 지정해 줄 수 있다.
useEffect(()=>{
},[inView])
ReactNode배열인 fragment를 화면에 배치하고 , 그 밑에 감시할 요소 ref를 두었다. ref의 높이가 없어서 감지를 못하는 것 같아 높이를 설정해주었다.
처음 화면이 렌더되면, fragment에는 아무것도 들어있지 않아서 ref가 바로 보일 것이고, inView가 true가 되어 useEffect훅이 동작할 것이다.
useEffect훅에서는 fragment에 검은 상자div를 추가하는데, useEffect훅이 fragment도 보고 있어서 inView가 화면밖으로 밀려날때 까지 검은 상자를 만들게 된다. 각 상자에는 key가 부여되는데, key가 0부터 1씩 늘어나서 100까지 동작하도록 조건을 주었다.
한마디로 이 코드는 스크롤 시 상자가 100개 될 때 까지 화면에 검은 상자가 꽉차게 렌더시키는 코드이다.
import { useState, useEffect } from "react";
import { useInView } from "react-intersection-observer";
export default function List() {
const { ref, inView } = useInView({
threshold: 0,
});
const [fragment, setFragment] = useState<React.ReactNode[]>([]);
const [key, setKey] = useState<number>(0);
useEffect(() => {
if (inView && key < 100) {
console.log(key, inView);
setKey((prev) => prev + 1);
const newF = <div key={key} className="m-10 h-10 w-10 bg-black"></div>;
setFragment((prev) => [...prev, newF]);
}
}, [inView, fragment]);
return (
<>
{fragment}
<div ref={ref} className="h-10"></div>
</>
);
}