infinite scroll
에서 scroll
이 특정 포지션을 지나갈 때, 아이템을 추가로 로드하기 위해 필요한 Intersection Observer API
.
Intersection Observer
: 타겟 엘리먼트와, 타겟 엘리먼트의 부모나 뷰포트가 교차하는 부분의 변화를 비동기적으로 관찰하는 API이다.
공식문서에서 말하는,
Intersection Observer API
는
document
에서의viewport
사이의 intersection
내의 변화를 Intersection Observer API
는
웹이 발전함에 따라 이러한 변화를 체크하는 것의 필요성이 높아졌고 그래서 나오게 된 API.
getBoundingClientRect()
로 실제 엘리먼트의 offset
등을 측정하는 방식으로 이루어졌는데, view
안에 인터섹션을 확인해줘야 하는 요소가 있다고 생각했을 때, 성능상의 문제를 가져올 수 있다. 😱😱export const useIntersection = () => {
const [isView, setIsView] = useState(false);
const elemRef = useRef<null | Element | undefined>(null);
const setRef = (elem: null | Element | undefined) => {
elemRef.current = elem;
};
useEffect(() => {
if (!elemRef.current) return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(
(entry) => {
if (entry.isIntersecting) {
setIsView(true);
}
},
{
rootMargin: '-50px',
threshold: 0.8,
}
);
});
observer.observe(elemRef.current);
return () => observer.disconnect();
}, []);
return [isView, setRef] as const;
};
const Components = () => {
const [isViewId, setIsViewId] = useState('0');
const elemRef = useRef<null[] | Element[]>([]);
const setRef = (elem: null | Element, i: number) => {
elemRef.current[i] = elem;
};
useEffect(() => {
if (elemRef.current.length === 0) return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(
(entry) => {
if (entry.isIntersecting) {
setIsViewId(entry.target.id);
}
},
{
rootMargin: '0',
threshold: 0.2,
}
);
});
elemRef.current.forEach((element) => {
observer.observe(element as Element);
});
return () => observer.disconnect();
}, []);
return (
<div>
{selections.map((item, i) => {
return (
<div key={item.id} ref={(e) => setRef(e, i)} >
// ... 생략
</div>
)
</div>
)
}
react-cool-inview
라이브러리로 좀 더 깔끔하고 쉽게.. 적용해보기!
yarn add react-cool-inview
# or
npm install --save react-cool-inview
import useInView from "react-cool-inview";
const App = () => {
const { observe, unobserve, inView, scrollDirection, entry } = useInView({
threshold: 0.25, // Default is 0
onChange: ({ inView, scrollDirection, entry, observe, unobserve }) => {
// Triggered whenever the target meets a threshold, e.g. [0.25, 0.5, ...]
unobserve(); // To stop observing the current target element
observe(); // To re-start observing the current target element
},
onEnter: ({ scrollDirection, entry, observe, unobserve }) => {
// Triggered when the target enters the viewport
},
onLeave: ({ scrollDirection, entry, observe, unobserve }) => {
// Triggered when the target leaves the viewport
},
// More useful options...
});
return <div ref={observe}>{inView ? "Hello, I am 🤗" : "Bye, I am 😴"}</div>;
};
import useInView from "react-cool-inview";
const App = () => {
const { observe, inView } = useInView({
onEnter: ({ unobserve }) => unobserve(),
});
return (
<div ref={observe}>
{inView ? "Hello, I am 🤗" : "Bye, I am 😴"}
</div>)
;
};