【React】データ更新時にスクロールを固定する
こんにちは、フリーランスエンジニアの太田雅昭です。
更新の前後でスクロール位置の固定
スクロール位置を固定するには、単純に考えれば以下のような流れとなります。
- 更新前のscrollHeightを保存
- 更新実行
- 現在のscrollHeightと更新前の値を計算
- 差分位置までスクロール
しかし実際はReactだとレンダリングのタイミングなども考慮しないといけません。計算のタイミングで、レンダリングが終わっている保証がないためです。そのため、下記のように修正します。
- 更新前のscrollHeightを保存
- 更新実行
- 下記を300ms間繰り返す
- 現在のscrollHeightと更新前の値を計算
- 差分位置までスクロール
またレンダリングを走らせるためにrequestAnimationFrameを使う必要もあります。
コード
下記のようになります。
function ScrollSample() {
// スクロール要素の参照
const scrollRef = useRef<HTMLDivElement>(null);
// データ取得関数
const refetch = () => new Promise<void>(resolve => setTimeout(resolve, 1000))
// スクロール位置を保持する関数
const keepScrollPosition = (params: { prevHeight: number }) =>
const timeout = 300;
new Promise<void>((resolve) => {
const { prevHeight } = params;
const start = performance.now();
const tick = () => {
const el = scrollRef?.current;
if (!el) return resolve();
if (!el.isConnected) return resolve();
if (performance.now() - start > timeout) return resolve();
el.scrollTo({
top: el.scrollHeight - prevHeight,
behavior: 'instant',
})
requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
});
// 更新実行
const handleClick = async () => {
const el = scrollRef?.current;
if (!el) return;
const prevHeight = el.scrollHeight;
await refetch();
await keepScrollPosition({ prevHeight });
}
return <>
<div ref={scrollRef}>
{/* ここにデータを入れる */}
</div>
<button onClick={handleClick} >更新実行</button>
</>
}