【React】特定の要素が画面内 / 画面より上 / 画面より下のどこにあるかを調べるカスタムフック

1 min read読了の目安(約1700字

Reactにおいて、特定の要素が現在

  • 画面内(ビューポート内)に存在する
  • 画面より上に存在する
  • 画面より下に存在する

のどれに該当するかをチェックするカスタムフックを作った(ものの使わなかった)ので供養しておきます。パフォーマンスのためにIntersection Observerを使っています。

カスタムフックを作成

useOnScreen.tsx
import React, { useState, useEffect } from 'react';

type TargetViewPosition = undefined | 'ABOVE_VIEWPORT' | 'BELOW_VIEWPORT' | 'VISIBLE';

export function useOnScreen(targetRef: React.RefObject<HTMLElement>) {
  const [targetViewPosition, setTargetViewPosition] = useState<
    TargetViewPosition
  >(undefined);

  const observer = new IntersectionObserver(
    ([entry]) => {
      if (entry.isIntersecting) {
        setTargetViewPosition('VISIBLE'); // 画面内に表示中
        return;
      }
      if (entry.boundingClientRect.top > 0) {
        setTargetViewPosition('BELOW_VIEWPORT'); // 画面より下に表示中
      } else {
        setTargetViewPosition('ABOVE_VIEWPORT'); // 画面より上に表示中
      }
    },
    {
      root: null,
      threshold: 0,
    }
  );

  useEffect(() => {
    // マウント時にobserverを登録
    if (targetRef.current) observer.observe(targetRef.current);
 
    // アンマウント時にobserverを解除
    return () => {
      observer.disconnect();
    };
  }, []);

  return targetViewPosition;
}

使い方

↓ カスタムフックの引数には画面上の位置を知りたい要素のrefを渡します。

SomeComponent.tsx
import { useOnScreen } from "./useOnScreen.tsx"

export const SomeComponent: React.VFC = () => {
  const targetViewPosition = useOnScreen();
  return (
    <>
      {targetViewPosition === 'VISIBLE' && <p>画面内に表示されています</p>}
      {targetViewPosition === 'ABOVE_VIEWPORT' && <p>画面より上に表示されています</p>}
      {targetViewPosition === 'BELOW_VIEWPORT' && <p>画面より下に表示されています</p>}
      <div ref={targetRef}>位置をチェックする対象の要素</div>
    </>
  )
}

あまり使いどころはないかもしれませんが。