⛓️
React 一番安定する自動スクロール
画面ローディング時にhtmlの一番下までスクロールしたい
こんちには、ハトです。業務で自動スクロールが必要になったので、いくつか方法を考えました。
- react-scrollでuseEffect(useLayoutEffect)ないでscrollToBottom()する
- react-windowというリストライブラリを使ってuseEffect時に一番下のアイテムまでscrollToItem()する
- リストの最後にdivタグをおき、refを取得。useLayoutEffect内で、ref.current.scrollIntoView()をする
useRefに関してはこちらの記事もご参考ください。
結論。refでscrollIntoViewをするのがよい
ミニマム
const List = () => {
const scrollBottomRef = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
// 以下はtypescriptの書き方。jsの場合は
// if(scrollBottomRef && scrollBottomRef.current) {
// scrollBottomRef.current.scrollIntoView()
// }
scrollBottomRef?.current?.scrollIntoView();
}, []);
const items: string[] = [];
return (
<div>
{items.map(item => (<div>{item}</div>))}
<div ref={scrollBottomRef}/>
</div>
)
}
データをフェッチしたあとの場合
const List = () => {
const [isLoading, setIsLoading] = useState(false);
const [items, setItems] = useState<string[]>([]);
const scrollBottomRef = useRef<HTMLDivElement>(null);
// データフェッチ
useEffect(() => {
// unmount時にはsetStateしない。フラグ。
let isUnmounted = false;
const load = async () => {
const items = await axiosLikeLibrary.get('https://example.com/items');
if(!isUnmounted){
setItems(items)
}
};
load();
return () => {
isUnmounted = true;
}
}, []);
// フェッチ後自動スクロール
useLayoutEffect(() => {
// 以下はtypescriptの書き方。jsの場合は
// if(scrollBottomRef && scrollBottomRef.current) {
// scrollBottomRef.current.scrollIntoView()
// }
scrollBottomRef?.current?.scrollIntoView();
}, []);
const items: string[] = [];
return (<>
{isLoading ? (
<div>ローディング中...</div>
) : (
<div>
{items.map(item => (<div>{item}</div>))}
{/* 一番下までスクロールするためのdiv↓ */}
<div ref={scrollBottomRef}/>
</div>
)}
</>)
}
他の選択肢はなぜだめなのか
react-scrollでuseEffect(useLayoutEffect)ないでscrollToBottom()する
これは難しいです。useEffectないではreact-scrollの animate scroll 機能は動きませんでした。結局useEffectないでsetTimeoutでいくらか時間をもうけてレンダリングをまってスクロールしていたのですが、レンダリングが終わる時間はリストの長さによってまちまちなので、そのやり方もうまくいきませんでした。
いかは関連するissue
Maybe the element isn't declared in the tree yet.
よくわからないのですが、useEffectないではツリーはまだ宣言されていない?みたいです。
react-windowというリストライブラリを使ってuseEffect時に一番下のアイテムまでscrollToItem()する
これでも実現できました。くわしくは以下
ただし、スクロールするだけにreact-windowを利用するのはオーバーすぎたのでやめました。
Discussion
scrollIntoView 知らなかったです...
ありがとうございます。
読んでたら、typo を見つけてしまいました...