7
1

More than 3 years have passed since last update.

React, Next.jsにおける無限スクロール

Posted at

はじめに

画面に描画するデータ件数が膨大で、読み込みに時間がかかるときには無限スクロールを使用するのがおすすめです。
無限スクロールとは、初回描画時はデータ全体のうちの一部だけ画面に描画しておいて、スクロールされたら描画するデータを少しずつ増やしていくというものです。

無限スクロール

  • 今回は描画するデータが全部で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>
  )
}

無限スクロール.gif

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1