Snippet - Intersection Observer API custom hook
当需要 React 中使用 Intersection Observer 來實現無限滾動時,可以創建一個 custom Hook。
創建 Custom Hook
import { useCallback, useState } from "react";
interface IntersectionOptions {
root?: Element | null;
rootMargin?: string;
threshold?: number | number[];
}
interface UseOnScreenHook {
measureRef: (node: Element | null) => void;
isIntersecting: boolean;
observer: IntersectionObserver | undefined;
}
const useOnScreen = ({
root = null,
rootMargin = "0px",
threshold = 0,
}: IntersectionOptions = {}): UseOnScreenHook => {
const [observer, setObserver] = useState<IntersectionObserver | undefined>();
// 目標元素是否在視窗可見
const [isIntersecting, setIntersecting] = useState(false);
const measureRef = useCallback(
(node: Element | null) => {
if (node) {
const intersectionObserver = new IntersectionObserver(
([entry]) => {
setIntersecting(entry.isIntersecting);
},
// 初始化 Intersection Observer
// root 表示根節點,預設是 null,表示用瀏覽器視窗作為根節點
// rootMargin 表示在根的邊界周圍加上margin,預設是0px
// threshold 表示元素可見度的闕值,介於 0 至 1 之間,預設為0,表示只要元素任何部分進入視窗就被視為可見(若設為1則全部進入才算可見)
{ root, rootMargin, threshold }
);
intersectionObserver.observe(node);
setObserver(intersectionObserver);
}
},
[root, rootMargin, threshold]
);
return { measureRef, isIntersecting, observer };
};
在元件中使用方式如下
const Component = () => {
const [hasMore, setHasMore] = useState(true);
const { measureRef, isIntersecting, observer } = useOnScreen();
const fetchData = useCallback(async (token: string) => {
setIsLoading(true);
try {
// CALL API 拿到資料,並進行資料處理
//倘若沒有資料則設setHasMore為false
setHasMore(false);
} catch (error) {
throw error;
} finally {
setIsLoading(false);
}
}, []);
useEffect(() => {
if (isIntersecting && hasMore && !isLoading) {
fetchData();
if (observer) {
observer.disconnect();
}
}
}, [isIntersecting, hasMore]);
return <div ref={measureRef}></div>;
};
這個元件使用 useOnScreen
來創建一個 measureRef,並將它綁定到一個 <div>
元素。當這個 <div>
元素進入視口時,isIntersecting 會變為 true,觸發 useEffect
中的條件判斷,進而觸發 fetchData() 來加載更多數據。