Refine v3 useList 排序实战:通过 config.sort 动态触发服务端排序请求
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
Refine v3 的数据 HookuseList是 TanStack QueryuseQuery的扩展,负责从指定resource拉取列表数据,并将分页、排序、过滤等配置透传给dataProvider的getList方法。本文以 v3 官方文档中useList的排序(Sorting)Live Preview 为例,完整讲解config.sort的用法、"排序参数变化自动触发新请求"的机制,并结合开源仓库中useList的源码实现说明其底层原理与参数流转路径。读完本文,你将能够:在任意 Refine 页面中实现可切换升/降序的列表、理解排序参数如何进入查询缓存键并驱动重新请求、以及掌握useList各配置项的完整参数表。
一、排序示例完整代码
排序是useList的核心能力之一。下面这段代码完整来自仓库中的排序 Live Preview 演示文件 sorting-live-preview.md,它展示了一个可点击切换升/降序的产品列表组件:
import { useState } from "react"; import { useList, HttpError } from "@pankod/refine-core"; interface IProduct { id: number; name: string; material: string; } const ProductList: React.FC = () => { const [order, setOrder] = useState<"asc" | "desc">("asc"); const { data, isLoading, isError } = useList<IProduct, HttpError>({ resource: "products", config: { sort: [ { field: "name", order, }, ], }, }); const products = data?.data ?? []; if (isLoading) { return <div>Loading...</div>; } if (isError) { return <div>Something went wrong!</div>; } return ( <div> <button onClick={() => setOrder((prev) => (prev === "asc" ? "desc" : "asc")) } > toggle sort </button> <ul> {products.map((product) => ( <li key={product.id}> <h4> {product.name} - ({product.material}) </h4> </li> ))} </ul> </div> ); };演示文件同时将该组件注册为products资源的list页面并渲染 Headless 版<Refine>骨架:
setRefineProps({ resources: [ { name: "products", list: ProductList, }, ], }); render(<RefineHeadlessDemo />);代码要点拆解:
useState持有排序方向:order的取值只能是"asc" | "desc"字面量联合,与sort配置项中order字段的类型严格一致。sort是数组结构:config.sort接收Array<{ field: string; order: "asc" | "desc" }>,数组顺序即多字段排序的优先级,上面的示例按name字段单字段排序。- 排序方向是响应式的:点击
toggle sort按钮更新order状态后,useList收到新的sort参数,自动发起一次新的getList请求。
二、useList的排序工作机制
排序部分在 v3 官方文档 useList/index.md 中的原始描述是:useList支持排序特性,传入sort属性即可启用排序;useList会将sort属性透传给dataProvider的getList方法处理;动态修改sort属性会触发一次新请求。
这句话背后有两条机制在支撑:
1. 查询键(Query Key)由参数生成,参数变了缓存键就变
文档明确说明:useList使用一个由传入属性生成的 query key 来缓存数据,可以通过 TanStack Query devtools 查看该 key。从当前仓库packages/core中useList的源码结构可以印证这一机制:查询键由dataProviderName + resource + action("list") + params逐段构建,其中 params 包含了 filters、pagination、sorters 等参数(见 useList.ts)。由于排序参数是缓存键的组成部分,order从"asc"变为"desc"时缓存键随之改变,TanStack Query 判定为"新查询",从而自动重新执行查询函数——这就是示例中"点按钮即刷新列表"的底层原因,无需手动调用 refetch。
2.queryFn将排序参数原样交给dataProvider.getList
查询函数的实现就是把resource、pagination、filters、排序参数与meta一起传给getList(见 useList.ts)。具体排序参数如何转成 URL query string(例如?sort=name&order=asc),完全取决于所用dataProvider的实现——resource通常被当作 API 端点路径,处理方式"完全取决于getList方法内部对resource的处理逻辑"(文档原话)。仓库中@refinedev/rest包内置了 nestjsx-crud、simple-rest、strapi-v4 等多种 provider,各自的options文件定义了排序参数的默认转换策略(如 nestjsx-crud.options.ts),并提供 handleSort 等工具函数及对应测试(handleSort.spec.ts),可以按 provider 逐一查看排序参数的落地格式。
三、config参数完整说明(v3 API)
v3 文档在 "Config Parameters" 一节给出了UseListConfig的完整接口定义,这是使用useList时最重要的参考契约:
interface UseListConfig { hasPagination?: boolean; pagination?: { current?: number; pageSize?: number; }; sort?: Array<{ field: string; order: "asc" | "desc"; }>; filters?: Array<{ field: string; operator: CrudOperators; value: any; }>; }各配置项的用途:
| 配置项 | 说明 | 示例 |
|---|---|---|
config.sort | 排序参数,透传给getList,用于向 API 发送排序 query 参数 | sort: [{ field: "title", order: "asc" }] |
config.filters | 过滤参数,向 API 发送过滤 query 参数,遵循CrudFilters接口 | filters: [{ field: "title", operator: "contains", value: "Foo" }] |
config.pagination.current | 当前页码 | pagination: { current: 2 } |
config.pagination.pageSize | 每页条数 | pagination: { pageSize: 20 } |
config.hasPagination | 是否启用服务端分页(不启用则一次性取回数据) | hasPagination: false |
三个参数(sort、filters、pagination)遵循同一套响应式规则:动态修改任意一个都会触发新的getList请求,因为它们是查询缓存键的组成部分。
useList除config外还支持以下顶层属性(均为可选):
| 属性 | 说明 | 示例 |
|---|---|---|
resource(必填) | 资源名,透传给getList,通常被用作 API 端点路径 | resource: "categories" |
dataProviderName | 存在多个 dataProvider 时指定使用哪一个 | dataProviderName: "second-data-provider" |
queryOptions | 透传给useQuery的额外选项 | queryOptions: { retry: 3 } |
metaData | 向 dataProvider 方法传递附加信息(如请求头),或用于以普通 JS 对象生成 GraphQL 查询 | metaData: { headers: { "x-meta-data": "true" } } |
successNotification | 需要NotificationProvider;数据拉取成功后调用其open展示成功通知,可自定义返回{ message, description, type } | 见下方示例 |
errorNotification | 需要NotificationProvider;拉取失败时展示错误通知,可自定义 | 见下方示例 |
liveMode | 需要LiveProvider;取"auto"或"manual",决定收到实时事件时是否自动更新数据 | liveMode: "auto" |
onLiveEvent | 需要LiveProvider;订阅事件到达时的回调 | onLiveEvent: (event) => console.log(event) |
liveParams | 需要LiveProvider;透传给liveProvider.subscribe的参数 | — |
通知回调示例:
useList({ successNotification: (data, values, resource) => { return { message: `${data.title} Successfully fetched.`, description: "Success with no errors", type: "success", }; }, });四、类型参数与返回值
类型参数(v3 API):
| 类型参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
TData | 查询结果数据类型,继承BaseRecord | BaseRecord | BaseRecord |
TError | 自定义错误对象类型,继承HttpError | HttpError | HttpError |
TData即示例中useList<IProduct, HttpError>的第一个参数,用来让data.data获得IProduct[]这样的精确类型推导。
返回值:useList直接返回 TanStack QueryuseQuery的QueryObserverResult<{ data: TData[]; total: number }, TError>,因此data、isLoading、isError、refetch等标准字段都可直接使用,示例中取data?.data得到记录数组、data?.total得到总数(服务端分页场景下)。
五、版本演进提示:v3 到当前 main 分支的 API 差异
需要注意本文代码与参数表对应的是v3 版本 API(包名@pankod/refine-core,文档路径位于 version-3.xx.xx)。当前仓库packages/core的主干源码已是后续大版本的实现,从源码结构看有两处明显的 API 演进:
sort更名为sorters:主分支的UseListProps中参数名为sorters?: CrudSort[],且不再是config的子字段,而是与filters、pagination平级的顶层参数(见 useList.ts)。同理metaData对应为meta。- 返回值结构变化:主分支
useList不再直接平铺useQuery结果,而是返回{ query, result, overtime }结构,其中result.data为记录数组(内部用EMPTY_ARRAY兜底空数据)、result.total为总数,query保留完整的useQuery返回值(见 useList.ts 与 useList.ts)。
如果你正在使用 v3 项目,请以本文的config.sort写法为准;若阅读主分支源码或测试(useList.spec.tsx)时看到sorters,两者语义一致,只是命名与层级调整。排序"参数变化即触发新请求"的核心机制——参数参与 query key 生成、queryFn透传给getList——在两个版本中保持一致。
六、小结
useList的排序通过config.sort: [{ field, order }]声明,order仅支持"asc" | "desc",数组顺序决定多字段排序优先级;- 排序参数是查询缓存键的组成部分,用
useState动态修改order即可让 TanStack Query 自动发起新的getList请求,无需手动 refetch; - 排序参数如何落地为 URL query 参数由各
dataProvider的getList实现决定,@refinedev/rest内置 provider 提供了可查阅的默认转换实现与测试; useList同时支持过滤、分页、metaData 透传、成功/失败通知与实时订阅等配置,类型参数TData/TError与useQuery返回值保证了完整的类型推导能力。
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考