この記事は「GraphQL with React入門 - QueryとMutationを学びPaginationの実装にチャレンジ!」(https://www.udemy.com/graphql-with-react/) で学んだ知見をまとめたものです。
自分がapollo x graphQLを使うにあたって非常に役立った動画ですので。初学者におすすめです。
とはGithubにも実装されているユーザーが検索した際のページネーションの実装スタイル
Connection
・・・ ある塊単位でデータを要求するその「ある塊」のことPage
・・・ Connectionが示す情報の集まり。Page
はedges
とpageInfo
の2つで構成されている。
Pageの中身のEdgesとpageinfo
Edge
・・・ 位置情報を示すCursor
と情報そのもののnode(データを抽象化した呼び名)
で構成されている。pageinfo
・・・startCursor
、endCursor
、hasPreviousPage
、hasNextPage
で構成されているstartCursor
・・・ 先頭のエッジがもつCursor情報(位置情報)endCursor
・・・最後尾のエッジがもつCursor情報hasPreviousPage
・・・前のPageが存在するかどうかhasNextPage
・・・次のPageが存在するかどうか
{
quer: "Query とは"//検索文字列
first: 5 //5件分のデータをください
}
// このpageの構成情報
{
endCursor: "Y3hogehoge=",
hasNextPage: true, //nextボタンの要否に使う
hasPreviousPage: false,
startCursor: "Y3eee="
}
### query
query searchRepogitriys($after: String, $before: String, $first: Int, $last: Int, $query: String!){
search(after: $after, before: $before, first: $first, last: $last, query: $query, type:REPOSITORY) {
repositoryCount
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
}
}
### variables
{
"after": null,
"before": null,
"first": 5,
"last": null,
"query": "フロントエンドエンジニア"
}
### return data
{
"data": {
"search": {
"repositoryCount": 22, //total22件のnodeがバックエンド側に存在するの意味
"pageInfo": {
"endCursor": "Y3Vyc29yOjU=",
"hasNextPage": true, //totalが6件以上あるのでtrue
"hasPreviousPage": false,
"startCursor": "Y3Vyc29yOjE="
}
}
}
}
以前の結果のafter値をendCorsorに指定して取得する
{
quer: "Query とは"//検索文字列
first: 5 //5件分のデータをください
after: "Y3hogehoge=" //カーソル情報。case1で実行で得られたendCursorの値を入れる。「ここより後ろのデータを要求します」の意味
before: null
last: null
}
// このpageの構成情報
{
endCursor: "Y3ggggg=",
hasNextPage: true,
hasPreviousPage: true,
startCursor: "Y3aaaa="
}
{
quer: "Query とは"//検索文字列
first: null
after: null
before: "Y3aaaa=" //前の結果で得られたstartcorsorを指定する。「ここより前の結果から5件分ください」の意味
last: 5 //方向が逆なので
}
//return data
{
endCursor: "Y3ggggg=",
hasNextPage: true, //前のページに遡っているので必ずここはtrueになる
hasPreviousPage: false,//もしも1ページ目ならfalse、 もしまだ遡れたらtrue
startCursor: "Y3bbbb="
}
query searchRepogitriys($after: String, $before: String, $first: Int, $last: Int, $query: String!){
search(after: $after, before: $before, first: $first, last: $last, query: $query, type:REPOSITORY) {
repositoryCount
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
edges { ## Pageとして要求した件数分エッジデータ
cursor ## エッジが持つ位置情報
node { ## 情報そのもの
...on Repository {
id
url
name
}
}
}
}
}
### return data
{
"data": {
"search": {
"repositoryCount": 22,
"pageInfo": {
"endCursor": "Y3Vyc29yOjU=",
"hasNextPage": true,
"hasPreviousPage": false,
"startCursor": "Y3Vyc29yOjE="
},
"edges": [
{
"cursor": "Y3Vyc29yOjE=",
"node": {
"id": "MDEwOlJlcG9zaXRvcnkxMjM5NTA3MjU=",
"url": "https://github.com/gipcompany/udemy-react-redux-crud-application",
"name": "udemy-react-redux-crud-application"
}
},
{
"cursor": "Y3Vyc29yOjI=",
"node": {
"id": "MDEwOlJlcG9zaXRvcnkxNjU4Mjg4OTE=",
"url": "https://github.com/a-oku-sharing-tech/frontend-work-space",
"name": "frontend-work-space"
}
},
{
"cursor": "Y3Vyc29yOjM=",
"node": {
"id": "MDEwOlJlcG9zaXRvcnkxMTI5MjQwOTk=",
"url": "https://github.com/kuro-channel/MyProfile",
"name": "MyProfile"
}
},
{
"cursor": "Y3Vyc29yOjQ=",
"node": {
"id": "MDEwOlJlcG9zaXRvcnkxMzMwNjMxOTE=",
"url": "https://github.com/ProgrammingSamurai/react-recipes",
"name": "react-recipes"
}
},
{
"cursor": "Y3Vyc29yOjU=",
"node": {
"id": "MDEwOlJlcG9zaXRvcnk4MDgwMzYyOQ==",
"url": "https://github.com/wakamor/fe-colosseum",
"name": "fe-colosseum"
}
}
]
}
}
}
各edgeが持つcorsor情報をデコードしてみる
const corsors = [
"Y3Vyc29yOjE=",
"Y3Vyc29yOjI=",
"Y3Vyc29yOjM=",
"Y3Vyc29yOjQ=",
"Y3Vyc29yOjU="
];
const result = corsors.map(e => {
return Buffer.from(e, "Base64").toString("binary");
});
console.log(result);
https://codesandbox.io/s/practical-ishizaka-wimn1?fontsize=14