はじめに
今回は、タイトルにある通り画面表示時、つまり任意のコンポーネントが表示された時にはすでに下端にスクロールされている状態にする方法について紹介していきたいと思います。
問題
LINEやDiscodeなどのチャット画面では、ページ表示時に一番下までスクロールされている状態が当たり前になっています。これをReactでやりたいというのが今回の目的です。
ここで試しに「React スクロール位置 下端」などで調べると、useRef
とuseEffect
を使用して、最下部にスクロールさせる方法がよく紹介されています。
しかしこの方法だと、下までスクロールするアニメーションが表示されてしまいます。
LINEやDiscodeにそんなアニメーションはないですよね。
最下部にスクロールされた状態で画面表示をしたいのです。
以下にだいぶ無理やりにそれを実現した方法を紹介します。
もっといい方法を本当に知りたいので、知っている方はコメントで教えてください🙇🏻♂️
解決方法
一言で言えば、スクロール中はdiv自体を非表示にしてあたかもスクロールアニメーションをないように見せる、とういだけのゴリ押し戦法です。
今回はデータフェッチにSWR
を使用しています。
const Chat: React.FC = () => {
// この部分はSWRを使用したカスタムフックなので関係はなし。
const {
data,
error,
isLoading,
} = useChat();
const endDiv = useRef<HTMLDivElement>(null);
const [isVisibility, setIsVisibility] = useState(false);
useEffect(() => {
// refがnullの時はスキップ
if (endDiv.current) {
endDiv.current.scrollIntoView();
setIsVisibility(true);
}
}, [data]);
return (
<>
<div
style={{
visibility: isVisibility ? "visible" : "hidden",
}}
>
// ここにチャットを展開
</div>
<div ref={endDiv} />
</>
);
};
各部の解説をしていきます。
const endDiv = useRef<HTMLDivElement>(null);
refはコンポーネントのDOMノードへの参照を持つオブジェクトです。
これを <div ref={endDiv} />
のようにdivに指定することでこのdivのDOMを参照することができます。
const [isVisibility, setIsVisibility] = useState(false);
こちらはdivの表示非表示を動的に切り替えるために用意します。
デフォルト値はfalse、つまり非表示です。
useEffect(() => {
// refがnullの時はスキップ
if (endDiv.current) {
endDiv.current.scrollIntoView();
setIsVisibility(true);
}
}, [data]);
useEffectでendDivのrefを持つdivまでスクロールさせます。
その後 setIsVisibility(true)
でdivを表示状態に更新します。
また、これはapiからのフェッチを行うのが前提なのでdata
を第二引数に指定し、data
がかわったタイミングで再レンダリングを行います。
今回はSWR
を使用しているので、定期的に再検証させているとしても、レスポンスのdataが変わっている(新しいチャットがあった場合)時のみ最下部まで自動でスクロールしてくれます。
<div
style={{
visibility: isVisibility ? "visible" : "hidden",
}}
>
{/*ここにチャットを展開*/}
</ div>
<div ref={endDiv} />
visibility: isVisibility ? "visible" : "hidden",
でisVisibilityによって表示非表示を切り替えます。
おわりに
かなり無理やりな実装ですが、とりあえずやりたかったことはできたので満足です。
もっといいやり方がみつかればまた更新したいと思います。
別なやり方等ありましたら是非コメントにお願いします🙏
では、最後までお読みいただきありがとうございました!