使用React和Ruby来实现类似页面编号的GraphQL页面分页功能
对于GraphQL的分页实现感觉很简单,但因为默认不提供获取页码的功能,所以经过一番尝试和困惑后,我总算勉强实现了带有(伪造的)页码的GraphQL分页功能。在此记录一下作为备忘。
如标题所示,该页面没有实际的页码。无法指定页码去查看页面内容,但可以显示当前所在位置。
用过的东西
-
- graphql: 14.5.8
-
- React: 16.12.0(Hooks使います)
-
- Ruby: 2.6.2
-
- Rails: 5.2.3
- material-ui/core”: 4.7.1
可以做的事情
-
- ページネーションでページ番号を表示(正確には何件目〜何件目の表示)
- 全件数の表示
我从Material-UI的文档中找到了这个内容,但是您如果看一下右下方,会发现显示着 “1-5 of 13″。这意味着显示了13件中的第1至第5件。我会将其实现。
做不到的事情 (Zhù bù de
-
- 指定したページ番号への遷移
- ページ内行数の指定
根据上图所示,我们不会实现通过页码进行页面导航(我不太懂如何实现)。
另外,我们也不会指定页面内的行数(即每页显示多少项)。(因为在这次项目中我不需要,所以没有实现。)
实施
GraphQL端
首先,我们需要编写GraphQL查询。
import gql from 'graphql-tag';
export const getDesserts = gql`
query desserts(
$first: Int
$after: String
$last: Int
$before: String
) {
desserts(
first: $first
after: $after
last: $last
before: $before
) {
totalCount
edges {
cursor
node {
id
calorie
fat
carb
protein
}
}
pageInfo {
endCursor
hasNextPage
startCursor
hasPreviousPage
}
}
}
`;
鼠标之类的说明可以通过搜索得到各种结果,所以稍微省略一下。
一般的GraphQL分页实现会返回edges和pageInfo,还会有一个名为totalCount的属性。一般情况下,这个属性是没有定义的,所以稍后会定义并确保它有正确的返回值。
因为目前没有定义,所以如果继续提交查询,会返回以下错误。因为没有定义!
{
"errors": [
{
"message": "Field 'totalCount' doesn't exist on type 'DessertsConnection'",
}
]
}
接下来,我们将实现自定义连接。
该连接提供了实现GraphQL分页功能(包括游标和页面信息等)的功能。
我们将在这个连接中添加total_count,以返回记录的总数。
class DessertsEdgeType < GraphQL::Types::Relay::BaseEdge
node_type(Types::DessertType)
end
class Types::DessertsConnection < GraphQL::Types::Relay::BaseConnection
field :total_count, Integer, null: false
def total_count
object.nodes.size
end
edge_type(DessertsEdgeType)
end
查询类型的实现如下。
module Types
class QueryType < Types::BaseObject
field :public_notifications, Types::DessertsConnection, null: false do
description 'A list of all desserts'
argument :first, Int, required: false # ページに表示するレコード数(nextで来た時)
argument :after, String, required: false # レコードにつくID。このIDの直後のレコードからfirst分の件数を表示する
argument :last, Int, required: false # ページに表示するレコード数(prevで来た時)
argument :before, String, required: false # レコードにつくID。このIDの直前のレコードからlast分の件数を表示する
end
def public_notifications()
Dessert.all
end
end
end
如果传递参数 first:5, after:’MQ32’,它将显示从ID为MQ32的记录之后的5条记录。(此ID是由GraphQL自动分配的。)
前端。
由于将所有表格都列举出来会使内容变多,所以我只摘录与分页有关的部分。
// 親コンポーネント
export const DessertsParent = props => {
const [queryVariables, setQueryVariables] = useState({
first: 10,
});
const [page, setPage] = useState(0);
// デザートのデータを取ってくるクエリ
const { data } = useDessertsQuery({
variables: { ...queryVariables },
});
const desserts = data;
// クエリに使う情報とページ番号の更新を行う関数
const paginationEventHandler = (recordRangeInfo, newPage) => {
setQueryVariables({ ...recordRangeInfo });
setPage(newPage);
};
return(
<>
<Child
desserts={desserts} // 表示するレコード
paginationEventHandler={paginationEventHandler} // ページネーションで使う関数
page={page} // ページ番号
/>
</>
);
}
// 子コンポーネント
import { TablePagination } from '@material-ui/core'
export const DessertsChild = props => {
const { desserts, paginationEventHandler, page } = props;
const handleChangePage = (event, newPage) => {
let directedPage;
if (page - newPage < 0) {
// next page
directedPage = { // 次のページを取得するための情報
after: desserts
? desserts.desserts.pageInfo.endCursor
: undefined,
first: 10,
};
} else {
// previous page
directedPage = { // 前のページを取得するための情報
before: desserts
? desserts.desserts.pageInfo.startCursor
: undefined,
last: 10,
};
}
paginationEventHandler(directedPage, newPage);
};
return(
<TablePagination
component="div"
count={
desserts ? desserts.desserts.totalCount : 0
}
onChangePage={handleChangePage}
page={page}
rowsPerPage={10} // 1ページに表示する件数(今回は10で固定)
rowsPerPageOptions={[10]} // 1ページに表示する件数のオプション(今回は10だけなので10のみ配列で渡す)
/>
)
我已经可以通过这个来获取数量以及下一页和上一页的信息。只要调整连接,就可以获取到各种信息,所以我想试一试。(包括页面编号等,只要调整这里就可以获取到)