はじめに
Webアプリケーションを開発する中であるHTMLの要素が画面内にあるかどうか知りたいことはよくあります。例えば無限にデータを読み込む必要がある時はスクロールが一番下のコンテンツまで到達したという情報、つまり一番下のコンテンツが画面内にあるかどうかが必要となります。他にも、対象のコンテンツがユーザーに表示された数を計測したい時にも使えます。
この記事ではある要素が画面内に現れた時にtrue
、存在しない時にfalse
を返すようなCustom Hooksを作成します。
実装
今回作成するコードの全景はこのようになっています。
import { RefObject, useEffect, useState } from 'react';
export const useInView = (ref: RefObject<HTMLElement>) => {
const [inView, setInView] = useState(false);
useEffect(() => {
if (!ref.current) {
return;
}
const observer = new IntersectionObserver(([entry]) =>
setInView(entry.isIntersecting),
);
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, [ref, setInView]);
return inView;
};
引数には監視対象の要素を参照するようなref
を渡します。
そして、useState
を用いて監視対象の要素が画面内に現れたらtrue
を返すような状態を作成します。ここで作成したinView
はこのhooksの返り値となります。
残りの部分は監視を行うように設定をするuseEffect
です。
useEffect(() => {
if (!ref.current) {
return;
}
const observer = new IntersectionObserver(([entry]) =>
setInView(entry.isIntersecting),
);
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, [ref, setInView]);
まずは、余計なことを考えないようにref.current
がFalsyな値(このケースではnull)の時は監視対象の要素がref
で参照されていないと判断して早期returnします。
そして、ref
が監視対象の要素を参照するようになったら、交差オブザーバーAPIを用いて画面内にref.current
で参照している要素が表示されているときはinView
をtrue
、表示されていないときはfalse
に設定させます。
この設定は異なるライフサイクル間で引き継ぐとバグの原因になるので、アンマウント時に解除されるようにクリーンアップ関数も書きます。
デモ
このhooksを利用方法の一例として以下のようなデモを作成しました。
refが画面内に現れている時だけ表示するメッセージが変わるようなコンポーネントになっています。
さいごに
ライブラリなどを利用せずに、ReactとブラウザAPIだけで簡単に作成することができるのでこれに近しいことがしたい場合はぜひご活用ください。