React技巧之檢查元素是否可見

chuck 發表於 2022-06-25
React

原文連結:https://bobbyhadz.com/blog/react-check-if-element-in-viewport

作者:Borislav Hadzhiev

正文從這開始~

總覽

在React中,檢查元素是否在視口範圍內:

  1. 在元素上設定ref屬性。
  2. 使用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傳遞的初始值為falseconst [isIntersecting, setIsIntersecting] = useState(false);

如果你想跟蹤鉤子的返回值的變化,請使用useEffect鉤子,並將該值新增到鉤子的依賴關係中。

const isInViewport1 = useIsInViewport(ref1);
console.log('isInViewport1: ', isInViewport1);

useEffect(() => {
  // 👇️ listen for changes
  console.log(isInViewport1);
}, [isInViewport1]);