본문 바로가기

programming/React

[React Custom Hook] 타겟 element 가 viewport 안에 보이는지 판단하는 hook (useVisibilityObserver)

1. 타겟 element 가 viewport 안에 보이는지 판단하는 hook

  • input
    • element: 관찰하고자 하는 타겟 element React.ref
    • rootMargin : 관찰 타겟 주위의 여백, new IntersectionObserver() 생성자에, option 으로 들어가는 값.
      • 예시. "10px 20px 30px 40px" (위, 오른쪽, 아래, 왼쪽). 값은 백분율이 될 수 있습니다. 기본값은 0.
      • 타겟 주변의 여백도 포함해서, 그 여백까지 타겟으로 보고, viewport 에서 사라졌는지 판단함.
      • IntersectionObserver.option.rootMargin 더보기
  • output
    • isVisible : 해당 타겟이 현재 viewport 안에 보이는지 여부 boolean.
    • observer : 생성한 IntersectionObserver 인스턴스
      • unmount 시키면서, 생성한 observer 인스턴스 관찰 unobserve 시키기 위함

 

useVisibilityObserver.ts

  • 코드
import { useState, useEffect, RefObject } from "react";

interface VisibilityObserverResult {
  isVisible: boolean;
  observer: IntersectionObserver | null;
}

const useVisibilityObserver = (
  element: RefObject<HTMLElement | null>,
  rootMargin: string,
): VisibilityObserverResult => {
  const [isVisible, setState] = useState(false);
  const [observer, setObserver] = useState<IntersectionObserver | null>(null);

  useEffect(() => {
    if (element.current) {
      const intersectionObserver = new IntersectionObserver(
        ([entry]) => {
          setState(entry.isIntersecting);
        },
        { rootMargin },
      );

      setObserver(intersectionObserver);

      intersectionObserver.observe(element.current);

      // Cleanup: Disconnect the observer when the component unmounts
      return () => {
        intersectionObserver.disconnect();
      };
    }
  }, [element, rootMargin]); // Make sure to include 'element' and 'rootMargin' in the dependency array

  return { isVisible, observer };
};

export default useVisibilityObserver;

 

 

MyComponent.tsx

  • 사용하는 컴포넌트 코드
const targetElRef = useRef<HTMLDivElement | null>(null);
const { isVisible: inViewport, observer } = useVisibilityObserver(targetElRef, "100px");

useEffect(() => {
  return () => {
    observer && targetElRef.current && observer.unobserve(targetElRef.current);
  };
}, [observer]);


...

return (
	...
	<div ref={targetElRef}>TEST</div>
	
	...
	
	<div style={{ margin: "10px", display: inViewport ? "block" : "none" }}>BOTTOM</div>
	...
)

 

  • 화면
    • TEST 박스가 화면안에 보일때는 BOTTOM 박스도 노출됨

  • TEST 박스가 화면 밖으로 사라지면 BOTTOM 글씨도 사라짐