UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【SWR】無限スクロール

swrを使っていて、無限スクロールを実現するにはどうしたらよいか調べた

ここでいう無限スクロールとは、スクロール量をトリガーにして、次ページのデータを取得し、現在のデータの末尾にそれらを追加することで、取得できるデータがある限り無限にスクロールできるようにするもの

調べたところ公式のドキュメントがあった

swr.vercel.app

大まかな実装方針としては1ページ単位の<Page>コンポーネントを用意し、それをページ数分ループさせるというもの

function App () {
  const [cnt, setCnt] = useState(1)

  const pages = []
  for (let i = 0; i < cnt; i++) {
    pages.push(<Page index={i} key={i} />)
  }

  return <div>
    {pages}
    <button onClick={() => setCnt(cnt + 1)}>さらに読み込む</button>
  </div>
}

ただ、このときトータルで何件表示されているのかはわからない

そこで、useSWRInfiniteを使ってトータルで何件表示されているかをわかるようにする

https://swr.vercel.app/ja/docs/pagination#useswrinfinite

ドキュメントをそのまま引用させてもらうと、使い方は以下

// 各ページの SWR キーを取得する関数であり、
// その返り値は `fetcher` に渡されます。
// `null` が返ってきた場合は、そのページのリクエストは開始されません。
const getKey = (pageIndex, previousPageData) => {
  if (previousPageData && !previousPageData.length) return null // 最後に到達した
  return `/users?page=${pageIndex}&limit=10`                    // SWR キー
}

function App () {
  const { data, size, setSize } = useSWRInfinite(getKey, fetcher)
  if (!data) return 'loading'

  // これで、すべてのユーザー数を計算できます
  let totalUsers = 0
  for (let i = 0; i < data.length; i++) {
    totalUsers += data[i].length
  }

  return <div>
    <p>{totalUsers} ユーザーがリストされています</p>
    {data.map((users, index) => {
      // `data` は、各ページの API レスポンスの配列です
      return users.map(user => <div key={user.id}>{user.name}</div>)
    })}
    <button onClick={() => setSize(size + 1)}>さらに読み込む</button>
  </div>
}

sizesetSizeがページ番号とページ番号の更新で、getKey関数でページ番号に応じたSWRキーを返すようにする

ページ番号が更新されるたびにdetaは以下のような構造で返されるので、これを使ってトータルの表示件数を算出する

[
  [
    { name: 'Alice', ... },
    { name: 'Bob', ... },
    { name: 'Cathy', ... },
    ...
  ],
  [
    { name: 'John', ... },
    { name: 'Paul', ... },
    { name: 'George', ... },
    ...
  ],
  ...
]