🧐

【React・GraphQL】無限スクロールを実装する!

2022/08/03に公開

https://github.com/danbovey/react-infinite-scroller
https://zenn.dev/syu/articles/1aeebcf13172d1
上を参考にしました。

インストール

npm install react-infinite-scroller --save

使用するファイルでimport文を加えます

import InfiniteScroll from 'react-infinite-scroller';

API実装

APIはlaravel lighthouseで実装します。自分は前回の記事をちょっとだけ改造しているだけです。
My APIを持っておくと勉強の時に便利です!
https://zenn.dev/jordan23/articles/f0568dab016f8a

無限スクロールを実装するために行なったのはたったこれだけで、paginateディレクティブを追加しただけです。

type Query {
    #ツイートを全て取得
    tweets: [Tweet!]! @paginate
}

https://lighthouse-php.com/4/api-reference/directives.html#paginate
ドキュメントによるとpaginateディレクティブを使うだけで、上記のクエリ定義が"以下のように定義された"と解釈できるようになります。

type Query {
  tweets(first: Int!, page: Int): TweetPaginator
}

type TweetPaginator {
  data: [Tweet!]!
  paginatorInfo: PaginatorInfo!
}

dataフィールドには取得したTweet情報、そしてpaginatorInfoにはページネーションに関する情報が格納されています。以下の画像を見て貰えばわかるように、どのような情報が入っているのか想像できます。今回はhasMorePagesを使って、取得できるTweetがあるかないかを判断しました。

tweets(first:3,page:1)とすることで、1ページ目として全体のデータから3件のTweetを取得できるようになります。今回はfirstの値を固定し、pageを可変にすることでスクロールのたびにクエリを投げ3件づつTweetを取得するということをしていきたいと思います。

無限スクロールを実装

app.tsx
import { useLazyQuery, useQuery } from "@apollo/client";
import React, { useState } from "react";
import InfiniteScroll from "react-infinite-scroller";
import { FETCH_TWEET } from "./gql/paginate";
import { useApolloClient } from "@apollo/client";

function App() {
  const [tweetList, setTweetList] = useState<any>([]);
  const [hasMore, sethasMore] = useState(true);
  const client = useApolloClient();

  const loadMore = async (page: number) => {
    const data = await client.query({
      query: FETCH_TWEET,
      variables: { page },
    });
    sethasMore(data.data.tweets.paginatorInfo.hasMorePages);
    setTweetList([...tweetList, ...data.data.tweets.data]);
    console.log(page);
    console.log(tweetList);
  };

  const items = (
    <ul>
      {tweetList.map((val: { content: string }) => {
        return <li>{val.content}</li>;
      })}
    </ul>
  );

  return (
    <div style={{ height: "500px", overflow: "auto" }}>
      <InfiniteScroll
        loadMore={loadMore}
        hasMore={hasMore}
        loader={<div>Tweetロード中...</div>}
        useWindow={false}
      >
        {items}
      </InfiniteScroll>
    </div>
  );
}

export default App;

無限スクロールを実装するにはInfiniteScrollタグで、ロードしたいアイテムを囲い、必要とされているPropsを指定してあげる必要があります。
Propsに関して詳しくはこちら(https://github.com/danbovey/react-infinite-scroller)に書いてあります。

loadMore

loadMoreについては、ユーザーがスクロールしてアイテムを要求した時のコールバックが指定されます。loadMoreに指定したコールバックの引数にはpage番号が指定され、呼ばれるたびに1から増えていきます。
上記のコードではページ番号をもとにクエリを投げ、返ってきたTweetをStateに順次保存しています。

hasMore

ロードするアイテムがあるか判断します。falseになったらスクロールを感知するリスナーは削除されます。上記コードではloadMoreコールバックにて、lighthouseで用意されたpaginatorInfoの情報からhasMorePagesフィールドの値をstateに保存して、InfiniteScrollのpropsに設定しています。

loader

ロードしている最中に表示できるコンポーネントを指定することができます。

useWindow

falseを指定することでoverflow:autoで正常に動くようになりました。
※trueだと中途半端なタイミングでずっとloadingとなってしまう。
https://cpoint-lab.co.jp/article/202009/16954/

実行結果

スクロールのたびに3件づつデータを取得することができています!

では!

Discussion