🍣

Reactで無限スクロールを実装する方法【react-infinite-scroller】

2020/12/03に公開1

ReactでWebページを作成していると無限スクロールを実装する機会があると思います。スクロールしたら非同期でデータを取得して、無限にスクロールできる機能のことです。YouTubeにも実装されているので見たことがあると思います。

今回はこの無限スクロールをReactで実装するライブラリのreact-infinite-scrollerを紹介します。

react-infinite-scrollerのインストール

まずはreact-infinite-scrollerのインストールを行います。以下のどちらかのコマンドを実行してインストールします。

npm install react-infinite-scroller --save

yarn add react-infinite-scroller

react-infinite-scrollerを使ってみる

インストールが完了したら、実際に使ってみましょう。次のサンプルは読みこむたびに数字が1つずつ大きくなるリストを作っています。スクロールバーを見ると縮んでいるので無限にスクロールできているのが分かりますよね。

import {useState} from 'react';
import InfiniteScroll  from "react-infinite-scroller"

export default function Index() {
  //表示するデータ
  const [list, setList] = useState([])
  
  //項目を読み込むときのコールバック
  const loadMore = (page) => {
    setList([...list, page])
  }

  //各スクロール要素
  const items = (
    <ul>
      {list.map((value) => <li>{value}</li>)}
    </ul>);
  
  //全体のスタイル
  const root_style = {
    marginLeft : "50px",
    marginTop : "50px",
  }

  //ロード中に表示する項目
  const loader =<div className="loader" key={0}>Loading ...</div>;

  return (
    <div style={root_style}>
      <InfiniteScroll
        loadMore={loadMore}    //項目を読み込む際に処理するコールバック関数
        hasMore={true}         //読み込みを行うかどうかの判定
        loader={loader}>      {/* 読み込み最中に表示する項目 */}

          {items}             {/* 無限スクロールで表示する項目 */}
      </InfiniteScroll>
    </div>
  )
}

react-infinite-scrollerの使い方は、簡単です。

まず初めに必要なライブラリーをインポートします。import InfiniteScroll from "react-infinite-scroller"で取り込みInfiniteScrollで使用します。

次に各属性を設定します。以下は、このライブラリーでよく使う属性の一覧です。

  • loadMore:ユーザーがスクロールしてコンテンツの読み込み時に実行される
  • pageStart:ロードする際のページ番号。loadMoreのコールバック関数の引数に渡される。
  • hasMore:ロードするコンテンツがまだあるかの判定。falseで読み込み終了。
  • loader:ロード中に表示する内容。

先ほどのコードでは、loadMore属性で読み込む際にlistにページ番号を追加していいます。そしてhasMore属性をtrueに設定することでスクロールしても無限に表示されるようになっています。

最後に、スクロール対象の項目を定義します。子コンポーネントに設定します。今回のソースでは随時、読み込まれて追加される配列をリスト形式で出力しています。これによりリストをスクロールしても無限に表示できるようなっているのです。

Web APIを使って無限スクロールを実現する

先ほどのサンプルはテスト用です。実際、使うとなるとWeb APIで作成することが多いでしょう。なのでここではWeb APIを使った実装方法を紹介します。

以下のソースはAPIから読み込んだ配列情報を出力しています。ただし、今回の場合は取得できなかった場合(pageが5以上)、読み込み処理を終了しています。

Web APIの処理(Next.js)

export default (req, res) => {
  const page = req.query.page //クエリパラメータの取得
  let result = [];
  if (page < 5) {
    //0~99を返す
    result = [...Array(100).keys()].map(i => i + page * 100)
  }

  //処理成功
  res.statusCode = 200
  res.json(result)
}

無限スクロール処理

import {useState} from 'react';
import InfiniteScroll  from "react-infinite-scroller"

export default function Index() {
  const [list, setList] = useState([]);          //表示するデータ
  const [hasMore, setHasMore] = useState(true);  //再読み込み判定
  
    //項目を読み込むときのコールバック
    const loadMore = async (page) => {
      
      const response = await fetch(`http://localhost:3000/api/test?page=${page}`);  //API通信
      const data = await response.json();  //取得データ

      //データ件数が0件の場合、処理終了
      if (data.length < 1) {
        setHasMore(false);
        return;
      }
      //取得データをリストに追加
      setList([...list, ...data])
    }

  //各スクロール要素
  const items = (
    <ul>
      {list.map((value) => <li>{value}</li>)}
    </ul>);
  
  //全体のスタイル
  const root_style = {
    marginLeft : "50px",
    marginTop : "50px",
  }

  //ロード中に表示する項目
  const loader =<div className="loader" key={0}>Loading ...</div>;

  return (
    <div style={root_style}>
      <InfiniteScroll
        loadMore={loadMore}    //項目を読み込む際に処理するコールバック関数
        hasMore={hasMore}      //読み込みを行うかどうかの判定
        loader={loader}>      {/* 読み込み最中に表示する項目 */}

          {items}             {/* 無限スクロールで表示する項目 */}
      </InfiniteScroll>
    </div>
  )
}

この処理では、loadMoreメソッド内のfetchでWeb APIのデータを取得して表示しています。引数にページ番号が渡るため、その値を使って対象のデータを追加しています。

そして、データが無ければhasMoreをfalseに設定し読み込みを終了することで無駄な通信をせずに済むようになっているのです。

まとめ

  • Reactで無限スクロールを使うならreact-infinite-scrollerがおススメ
  • loadMoreに読み込み時のコールバック関数を設定する
  • hasMoreで読み込みを行うかどうかの判定を行う

参考

react-infinite-scroller

Discussion