原文連結:https://bobbyhadz.com/blog/react-check-if-element-in-viewport
正文從這開始~
總覽
在React中,檢查元素是否在視口範圍內:
- 在元素上設定
ref
屬性。 - 使用
IntersectionObserver
API來跟蹤元素是否與視口相交。
import {useEffect, useRef, useState, useMemo} from 'react';
export default function App() {
const ref1 = useRef(null);
const ref2 = useRef(null);
const isInViewport1 = useIsInViewport(ref1);
console.log('isInViewport1: ', isInViewport1);
const isInViewport2 = useIsInViewport(ref2);
console.log('isInViewport2: ', isInViewport2);
return (
<div>
<div ref={ref1}>Top div {isInViewport1 && '| in viewport ✅'}</div>
<div style={{height: '155rem'}} />
<div ref={ref2}>Bottom div {isInViewport2 && '| in viewport ✅'}</div>
</div>
);
}
function useIsInViewport(ref) {
const [isIntersecting, setIsIntersecting] = useState(false);
const observer = useMemo(
() =>
new IntersectionObserver(([entry]) =>
setIsIntersecting(entry.isIntersecting),
),
[],
);
useEffect(() => {
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, [ref, observer]);
return isIntersecting;
}
該示例向我們展示了,如何檢查元素是否在視口範圍內。IntersectionObserver
API使我們能夠檢查一個給定的元素是否與文件相交。
useIsInViewport
鉤子接收一個指向我們想要追蹤的元素的ref
物件。
IntersectionObserver
IntersectionObserver
建構函式接收一個函式,該函式被呼叫時帶有一個entry
陣列。entry
是一個陣列,其包含了所有的obeserver
的目標元素。這些元素的可見度已經高於或低於intersection observer
的比率之一。
每個entry
都描述了一個給定元素與根元素(文件)相交的程度。我們解構了這個entry
,因為我們的IntersectionObserver
只能跟蹤一個元素(就是我們設定ref
的那個元素)。
我們呼叫observe()
方法,將我們要跟蹤的元素傳給它 - observer.observe(ref.current)
。
每當元素進入視口或者存在於視口中時,我們傳遞給IntersectionObserver()
建構函式的函式就會被呼叫,然後更新state
變數。
// ?️ gets called every time element enters or leaves viewport
new IntersectionObserver(([entry]) =>
setIsIntersecting(entry.isIntersecting),
)
如果我們設定ref
物件的元素在視口中,useIsInViewport
鉤子將會返回true
。如果元素不在視口中,該鉤子將會返回false
。
需要注意的是,在初始渲染時,useIsInViewport
鉤子將會返回false
。因為我們為useState
傳遞的初始值為false
。const [isIntersecting, setIsIntersecting] = useState(false);
如果你想跟蹤鉤子的返回值的變化,請使用useEffect
鉤子,並將該值新增到鉤子的依賴關係中。
const isInViewport1 = useIsInViewport(ref1);
console.log('isInViewport1: ', isInViewport1);
useEffect(() => {
// ?️ listen for changes
console.log(isInViewport1);
}, [isInViewport1]);