本文是graphql极简入门教程的第五篇,本篇内容主要讲述前后端添加分页及排序功能
graphql极简入门教程目录:
- 第一篇:基于react和graphql-yoga搭建前后端,并实现一个hello world
- 第二篇:基于prisma及sqlite,通过playground创建及查询数据
- 第三篇:在react中执行graphql的新增和查询操作
- 第四篇:react添加路由导航、前后端搜索功能
- 第五篇:添加分页及排序功能
- 第六篇:后端编写用户登录及注册功能
- 第七篇:前端对接用户系统
- 第八篇:前后端接入github的Oauth系统
- 第九篇:graphql实时订阅
分页、排序功能
定义后端数据类型
对于分页功能通常的做法有两种:
- 一种是通过给后端传递起始索引(offset)以及偏移量(limit)来实现
- 另一种是通过提供元素的游标(cursor)即当前页第一行元素ID,以及需要的元素数量来实现
在这里笔者为了方便,采用第一种方案,在具体的实现中:
take
字段代表需要获取n
个元素,也就是上面说的偏移量(limit)skip
字段代表需要跳过n
*take
个元素,也就是上面说的起始索引(offset)
在分页的需求中,通常还会返回数据总量,因此还需要添加count
字段。
同时也会有按照某个字段排序的需求,你需要一个input
类型以及枚举(enum
)类型来实现:
input LinkOrderByInput {
description: Sort
url: Sort
createdAt: Sort
}
enum Sort {
asc
desc
}
在上面的例子中,创建了一个input
类型,包含所有需要的排序的字段,在enum
类型中定义了正序及逆序的字段。
接下来修改src/server/schema.graphql
文件,将上面的定义写入:
type Query {
- feed(filter: String): Feed!
+ feed(filter: String, skip: Int, take: Int, orderBy: LinkOrderByInput): Feed!
}
+ input LinkOrderByInput {
+ description: Sort
+ url: Sort
+ createdAt: Sort
+ }
+ enum Sort {
+ asc
+ desc
+ }
type Link {
id: ID!
url: String!
description: String!
createdAt: DateTime!
}
type Feed {
links: [Link!]!
+ count: Int!
}
scalar DateTime
type Mutation {
post(url: String!, description: String!): Link!
}
接下来,需要添加上面查询的具体实现,修改src/server/resolvers/Query.js
文件:
async function feed(parent, args, context) {
const where = args.filter
? {
OR: [
{ description: { contains: args.filter } },
{ url: { contains: args.filter } }
]
}
: {};
// ①
const links = await context.prisma.link.findMany({
where,
skip: args.skip,
take: args.take,
orderBy: args.orderBy
});
// ②
const count = await context.prisma.link.count({ where });
return {
links,
count
};
}
module.exports = {
feed
};
在①中,向prisma
提供的findMany
接口传入了分页及排序的相关信息,就可以查询出预期的结果
在②中,通过prisma
提供的count
接口来计算当前查询条件下,一共有多少条数据。
最终按照定义好的数据结构类型返回查询结果即可。
前端接入分页、排序逻辑
接下来,需要修改列表页面,接入分页和排序逻辑,修改src/components/LinkList.js
文件:
import React, {useCallback, useMemo, useState} from 'react';
import Link from './Link';
import {useQuery} from "urql";
// ①
const feedQuery = `
query FeedQuery($take: Int, $skip: Int, $orderBy: LinkOrderByInput) {
feed(take: $take, skip: $skip, orderBy: $orderBy) {
count
links {
id
createdAt
url
description
}
}
}
`;
// ②
const PAGE_SIZE = 2;
const LinkList = () => {
const [page, setPage] = useState(1);
const [sort, setSort] = useState('desc');
// ③
const skip = useMemo(() => {
return (page - 1) * PAGE_SIZE
}, [page]);
const [result] = useQuery({
query: feedQuery,
// ④
variables: { skip, take: PAGE_SIZE, orderBy: { createdAt: sort } },
});
const { data, fetching, error } = result;
// ⑤
const nextPage = useCallback(() => {
if (page <= data?.feed?.count / PAGE_SIZE) {
setPage(page + 1)
}
}, [data?.feed?.count, page]);
// ⑥
const previousPage = useCallback(() => {
if (page > 1) {
setPage(page - 1)
}
}, [page]);
return (
<div>
{fetching && <p>Loading...</p>}
{error && <pre>{JSON.stringify(error, null, 2)}</pre>}
{data && (
<>
<button
onClick={() => {
// ⑦
setSort(sort === 'desc' ? 'asc' : 'desc');
}}
>
{/*⑦*/}
{ sort === 'desc' ? 'change to older' : 'change to latest' }
</button>
{
data.feed.links.map((link) => (
<Link key={link.id} link={link} />
))
}
<div className="flex ml4 mv3 gray">
<div className="pointer mr2" onClick={previousPage}>
Previous
</div>
<div className="pointer" onClick={nextPage}>
Next
</div>
</div>
</>
)}
</div>
);
};
export default LinkList;
这里的改动较大,需要一步步进行分析:
在①中,按照之前的后端数据定义结构类型,定义前端数据结构类型,新增传入take
、skip
、orderBy
字段,分别执行分页和排序功能。
由于目前数据较少,为了方便演示,②将当前分页的数量设置为2
。
根据当前所在页数,③里定义了计算偏移量的方法:将当前页面减去一除以数量既可以得到当前的偏移量。
④将分页数据及排序数据变量传入useQuery
钩子中,列表默认按照时间倒序排序。
⑤和⑥方法处理了在翻页时,前进和后退时,需要设置当前所在的页面是第几页。
⑦中允许用户切换排序规则,按照时间倒序或者顺序排序。
打开http://localhost:3000/页面,效果如下👇🏻
你可以点击Previous
和Next
前进和后退
也可以点击change to older
切换到按照时间顺序排序,点击change to latest
切换到按照时间倒序排序。
你已经完成了排序的筛选的功能,恭喜(^▽^),下一章我们将会接入用户系统
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情