Apollo Client の Pagination 機能
GraphQL API では柔軟なクエリ発行が可能ですが、多数の要素を取得する場合は、Pagenation 処理 により何度かに分けて API 呼び出しを行う必要があります。 例えば、GitHub の GraphQL API では一度のクエリで取得可能な要素数は 100 件までであり、それを超える情報を取得する場合に Pagination 処理が必要です。
Apollo Client には、GraphQL の Pagination 処理を簡単に扱うための仕組み (fetchMore
) が用意されています。
と言っても、そこまで簡単ではないので、ここでは GitHub の GraphQL API における Pagination 処理の具体的な実装例を紹介します。
Pagination の実装例
次のサンプルコードは、GitHub の myorg/myrepo
リポジトリの Issue リストを表示する IssueList
コンポーネントの実装例です。
Issue の数が 100 件を超える場合は、「さらに読み込む」ボタンを表示し、このボタンが押されたときに Pagination 処理(fetchMore
関数)で次のデータを取得するようにしています。
import * as React from 'react';
import {gql, useQuery} from '@apollo/client';
// GraphQL クエリ
const QUERY_ISSUE_LIST = gql`
query queryIssueList($cursor: String) {
search(first: 100, after: $cursor, type: ISSUE,
query: "repo:myorg/myrepo is:issue is:open") {
nodes {
... on Issue { id number title }
}
pageInfo { hasNextPage endCursor }
}
}
`;
// "さらに読み込む" ボタン
function createFetchMoreButton(pageInfo, fetchMore) {
if (!pageInfo.hasNextPage) {
return null;
}
return (
<button onClick={() => {
fetchMore({
variables: {cursor: pageInfo.endCursor},
updateQuery: (prevResult, {fetchMoreResult}) => {
if (!fetchMoreResult) return prevResult;
return fetchMoreResult;
}
});
}}>さらに読み込む</button>
);
}
export const IssueList: React.FC = () => {
const {loading, error, data, fetchMore} = useQuery(QUERY_ISSUE_LIST);
if (loading) return <p>Loading ...</p>;
if (error) return <p style={{color: 'red'}}>{error.message}</p>;
const {search} = data;
const {pageInfo} = search;
return <>
<ul>
{search.nodes.map(x => (
<li key={x.id}>{x.number}: {x.title}</li>
))}
</ul>
{createFetchMoreButton(pageInfo, fetchMore)}
</>;
};
ポイントは、Apollo Client の useQuery
フックが返す fetchMore
関数の使い方です。
上記の例では、「さらに読み込む」ボタンが押された時に、この fetchMore
関数を呼び出しています。
fetchMore({
variables: {cursor: pageInfo.endCursor},
updateQuery: (prevResult, {fetchMoreResult}) => {
if (!fetchMoreResult) return prevResult;
return fetchMoreResult;
}
});
fetchMore
関数を呼び出すと、variables
パラメータで指定した変数値を使って再度 GraphQL クエリが実行され、updateQuery
パラメータで指定した関数が返す値で再描画が行われます。
fetchMoreResult
には、新しいクエリでサーバーから返された値が格納されているため、これをそのまま返すことで、次のページの情報を描画することができます。
もし、前回取得したデータとマージして表示したいのであれば、次のような感じで、戻り値をうまいこと加工してやります。
この例では、search.nodes
のフィールドをマージしています。
fetchMore({
variables: {cursor: pageInfo.endCursor},
updateQuery: (prevResult, {fetchMoreResult}) => {
if (!fetchMoreResult) {
return prevResult;
}
return {
search: {
__typename: fetchMoreResult.search.__typename,
issueCount: fetchMoreResult.search.issueCount,
nodes: [...prevResult.search.nodes, ...fetchMoreResult.search.nodes],
pageInfo: fetchMoreResult.search.pageInfo,
}
};
}
});
search
フィールドの値を漏れなく列挙するのが面倒な場合は、Object.assign()
による Shallow マージの仕組みを使って、次のように記述することもできます。
return {
search: Object.assign({}, fetchMoreResult.search, {
nodes: [...prevResult.search.nodes, ...fetchMoreResult.search.nodes]
})
};
関連記事
- Apollo Client で GitHub GraphQL API を使う (Node & React)
- GitHub GraphQL クエリ例: マイルストーン情報を取得する (milestone)
- GitHub GraphQL クエリ例: PullRequest の情報を取得する (search)
- GitHub GraphQL クエリ例: イシュー情報を取得する (search)
- GitHub GraphQL クエリ例: リポジトリの情報を取得する (repository)
- GitHub GraphQL クエリ例: 組織の情報を取得する (organization)
- GitHub の GraphQL API Explorer の使い方