はじめに
画面に描画するデータ件数が膨大で、読み込みに時間がかかるときには無限スクロールを使用するのがおすすめです。
無限スクロールとは、初回描画時はデータ全体のうちの一部だけ画面に描画しておいて、スクロールされたら描画するデータを少しずつ増やしていくというものです。
無限スクロール
- 今回は描画するデータが全部で50万件あることを想定して作ってみました。初回は50万件のうち100件だけ描画し、スクロール位置が8割まで行ったらまた100件追加して描画する。それを繰り返すっていう感じです
infinite-scroll.tsx
import { useEffect, useState, useRef } from 'react'
import styles from 'pages/infinite-scroll.module.scss'
interface Item {
id: number
content: string
}
export default function InfiniteScroll() {
const [allItems, setAllItems] = useState<Item[]>([])
const [items, setItems] = useState<Item[]>([])
const container = useRef(null)
const onScroll = () => {
const el = container.current
const rate = el.scrollTop / (el.scrollHeight - el.clientHeight)
// スクロール位置の割合が8割を超えている場合は描画するアイテムを追加
if (rate > 0.8) {
setItems((prevItems) => {
const newItems = [
...prevItems,
...allItems.slice(prevItems.length, prevItems.length + 100),
]
return newItems
})
}
}
useEffect(() => {
const data: Item[] = []
// 50万件の配列を用意
for (let i = 0; i < 50000; i++) {
data.push({ id: i + 1, content: `内容${i + 1}` })
}
setAllItems(data)
setItems(data.slice(0, 100))
}, [])
return (
<div className={styles.container} ref={container} onScroll={onScroll}>
{/* この部分をitemsからallItemsに変更すると、初回読み込みのスピードの差が歴然です! */}
{items.map((item) => (
<div key={item.id}>{item.content}</div>
))}
</div>
)
}