grahql极简入门教程(基于react+graphql-yoga+urql)⑤

183 阅读4分钟

本文是graphql极简入门教程的第五篇,本篇内容主要讲述前后端添加分页及排序功能

graphql极简入门教程目录:

👉🏻点击进入本教程github仓库

分页、排序功能

定义后端数据类型

对于分页功能通常的做法有两种:

  • 一种是通过给后端传递起始索引(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;

这里的改动较大,需要一步步进行分析:

在①中,按照之前的后端数据定义结构类型,定义前端数据结构类型,新增传入takeskiporderBy字段,分别执行分页和排序功能。

由于目前数据较少,为了方便演示,②将当前分页的数量设置为2

根据当前所在页数,③里定义了计算偏移量的方法:将当前页面减去一除以数量既可以得到当前的偏移量。

④将分页数据及排序数据变量传入useQuery钩子中,列表默认按照时间倒序排序。

⑤和⑥方法处理了在翻页时,前进和后退时,需要设置当前所在的页面是第几页。

⑦中允许用户切换排序规则,按照时间倒序或者顺序排序。

打开http://localhost:3000/页面,效果如下👇🏻

image-20230207223948984

你可以点击PreviousNext前进和后退

也可以点击change to older切换到按照时间顺序排序,点击change to latest切换到按照时间倒序排序。

你已经完成了排序的筛选的功能,恭喜(^▽^),下一章我们将会接入用户系统

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情