资讯动态

使用 Refine useList 实现资源列表过滤:filters 参数深度实战指南

发布时间:2026/9/12 2:38:45 来源:尧图企业网站定制
使用 Refine useList 实现资源列表过滤filters 参数深度实战指南【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine导读本文围绕 Refine v5 的useListHook 及其filters参数展开讲解如何在列表数据获取中实现按字段、运算符的动态过滤并结合packages/core的源码实现、类型定义与测试用例说明过滤器如何从组件一路传递到 data provider 的getList方法帮助读者掌握可控、可复现的过滤方案。一、useList与过滤功能概述在 Refine 中useList是 TanStack Query 的useQuery的扩展版本在继承其全部能力缓存、重试、失效等的基础上增加了面向资源列表的语义化参数。当需要按照排序sorters、过滤filters、分页pagination等条件从resource获取数据时即可使用该 Hook。它的核心工作方式可以总结为两点见 index.md使用dataProvider的getList方法作为查询函数query function使用由传入属性生成的query key缓存数据可在 TanStack Query Devtools 中查看。过滤功能对应useList的filters属性。动态改变filters会触发新的请求——这正是本篇文章要展开的主题。仓库中对应功能的实时示例位于 _filtering-live-preview.md本文将以该示例为主体进行讲解。二、一个完整的动态过滤示例以下代码来自 _filtering-live-preview.md它演示了「根据下拉框选择实时过滤商品列表」的完整场景import { useState } from react; import { useList, HttpError } from refinedev/core; interface IProduct { id: number; name: string; material: string; } const ProductList: React.FC () { const [value, setValue] useState(Cotton); const { result, query } useListIProduct, HttpError({ resource: products, filters: [ { field: material, operator: eq, value, }, ], }); const products result.data ?? []; if (query.isLoading) { return divLoading.../div; } if (query.isError) { return divSomething went wrong!/div; } return ( div span material: /span select value{value} onChange{(e) setValue(e.target.value)} {[Cotton, Bronze, Plastic].map((material) ( option key{material} value{material} {material} /option ))} /select ul {products.map((product) ( li key{product.id} h4 {product.name} - ({product.material}) /h4 /li ))} /ul /div ); };示例同时配置了路由与资源完整运行需要这部分配置setInitialRoutes([/products]); setRefineProps({ resources: [ { name: products, list: /products, }, ], }); render( ReactRouter.BrowserRouter RefineHeadlessDemo ReactRouter.Routes ReactRouter.Route path/products element{ProductList /} / /ReactRouter.Routes /RefineHeadlessDemo /ReactRouter.BrowserRouter, );这个例子的关键点在于value通过useState管理与select双向绑定filters数组中的value直接引用 React 状态下拉框切换时setValue改变状态filters随之变化useList会携带新的过滤条件重新请求通过query.isLoading与query.isError分别处理加载中和出错状态使用result.data类型为IProduct[]渲染列表其内部数据来自query的响应。需要注意这里的operator: eq表示精确相等匹配这是 Refine 中大量内置运算符之一完整运算符见下文。三、filters参数的结构与运算符体系filters会被原样传递给getList方法index.md 中 Filtering 一节明确说明用于向 API 发送过滤查询参数。其基本结构是一个过滤条件对象数组useList({ filters: [ { field: title, operator: contains, value: Foo, }, ], });3.1 类型定义LogicalFilter 与 ConditionalFilter在 packages/core/src/contexts/data/types.ts 中过滤条件被划分为两类export type LogicalFilter { field: string; operator: ExcludeCrudOperators, or | and; value: any; }; export type ConditionalFilter { key?: string; operator: ExtractCrudOperators, or | and; value: (LogicalFilter | ConditionalFilter)[]; }; export type CrudFilter LogicalFilter | ConditionalFilter; export type CrudFilters CrudFilter[];LogicalFilter逻辑过滤单字段、单运算符的条件是绝大多数场景下的用法ConditionalFilter条件组合通过or/and运算符嵌套组合多个子条件用于表达复杂查询CrudFilters即CrudFilter[]也就是useList的filters参数类型。3.2 完整的运算符列表同一文件types.ts中定义了CrudOperators联合类型涵盖以下运算符运算符含义eq/ne等于 / 不等于eqs/nes等于大小写敏感/ 不等于大小写敏感lt/gt/lte/gte小于 / 大于 / 小于等于 / 大于等于in/nin属于 / 不属于数组ina/nina属于大小写敏感/ 不属于大小写敏感contains/ncontains包含 / 不包含containss/ncontainss包含大小写敏感/ 不包含大小写敏感between/nbetween介于 / 不介于null/nnull为空 / 不为空startswith/nstartswith以…开头 / 不以…开头startswiths/nstartswiths以…开头大小写敏感/ 不以…开头大小写敏感endswith/nendswith以…结尾 / 不以…结尾endswiths/nendswiths以…结尾大小写敏感/ 不以…结尾大小写敏感or/and条件组合运算符仅用于 ConditionalFilter这一整套运算符由 Refine 内置定义具体某个运算符最终是否生效取决于所选 data provider 对filters的解析与请求参数映射实现例如refinedev/simple-rest、refinedev/rest等包各自实现了将CrudFilters转换成查询字符串或请求体的逻辑。四、useList中过滤的底层实现链路理解了参数结构后我们来看filters在源码中的完整流转过程。核心实现位于 packages/core/src/hooks/data/useList.ts。4.1 参数接收与资源解析useList通过useResourceParams解析resource支持直接传入名称或使用identifier匹配并通过useDataProvider与pickDataProvider选择实际使用的 data provideruseList.tsconst { resources, resource, identifier } useResourceParams({ resource: resourceFromProp, }); const dataProvider useDataProvider(); // ... const pickedDataProvider pickDataProvider(identifier, dataProviderName, resources); const prefferedFilters filters; // ... const { getList } dataProvider(pickedDataProvider);过滤条件会被原样保留prefferedFilters并在查询函数中传递给getList。4.2 query key 与过滤联动useList基于属性构建查询键其中过滤条件直接影响 query keyuseList.tsqueryKey: keys() .data(pickedDataProvider) .resource(identifier ?? ) .action(list) .params({ ...(preferredMeta || {}), filters: prefferedFilters, ...(isServerPagination { pagination: prefferedPagination }), ...(sorters { sorters }), }) .get(),这意味着当filters中的字段、运算符或值发生变化时query key 也随之变化TanStack Query 会据此发起一次全新的请求。这也解释了为什么「动态改变filters会触发新请求」——这是过滤功能与 React 状态天然协同的根本原因。4.3 请求发送filters 传给 getList查询函数queryFn中filters与pagination、sorters、meta一起被传给getListuseList.tsqueryFn: (context) { const meta { ...combinedMeta, ...prepareQueryContext(context), }; return getListTQueryFnData({ resource: resource?.name ?? , pagination: prefferedPagination, filters: prefferedFilters, sorters: prefferedSorters, meta, }); },最终由具体 data provider 将过滤条件翻译成 API 可识别的参数如 REST 查询字符串、GraphQL 查询条件等。4.4 返回值结构useList返回的对象中useList.tsqueryTanStack Query 的QueryObserverResult提供isLoading、isError、data等状态result解包后的数据data为数组无数据时返回冻结的空数组EMPTY_ARRAYtotal为总行数overtime请求超时信息elapsedTime。五、复杂过滤用or/and组合条件当业务需要「A 或 B」这类条件时可以嵌套ConditionalFilter。例如过滤出material为Cotton或Plastic的商品useListIProduct, HttpError({ resource: products, filters: [ { operator: or, value: [ { field: material, operator: eq, value: Cotton }, { field: material, operator: eq, value: Plastic }, ], }, ], });从类型定义可以看出ConditionalFilter的value是递归结构支持任意深度的嵌套组合。这类结构化过滤器同样会在 query key 中参与缓存标识因此组合条件变化时也会自动重新请求。六、测试用例对过滤行为的验证仓库中的单元测试确认了filters会被原样传给getList。在 packages/core/src/hooks/data/useList.spec.tsx 中测试用例断言了调用参数useList({ resource: posts, filters: [{ field: id, operator: eq, value: 1 }], pagination: { mode: client, currentPage: 10, pageSize: 5 }, sorters: [{ field: id, order: asc }], }); // ... expect(getListMock).toHaveBeenCalledWith( expect.objectContaining({ filters: [{ field: id, operator: eq, value: 1 }], pagination: { mode: client, currentPage: 10, pageSize: 5 }, sorters: [{ field: id, order: asc }], }), );该测试以「过滤条件被透传给 data provider 的 getList」为断言目标从测试层面佐证了本文第 4 节描述的调用链。七、常用配套参数速查围绕过滤场景useList还常与以下参数搭配使用完整参数见 index.md参数说明示例resource必填资源名通常作为 API 端点路径resource: categoriesdataProviderName多 data provider 时指定使用哪一个dataProviderName: second-data-providerpagination分页参数currentPage、pageSize、modeoff/client/serverpagination: { mode: off }sorters排序参数数组sorters: [{ field: title, order: asc }]queryOptions透传给useQuery的额外选项如retry、enabledqueryOptions: { retry: 3 }meta传给 data provider 的附加信息如自定义 headers、GraphQL 查询构造meta: { headers: { x-meta-data: true } }liveMode/onLiveEvent/liveParams实时订阅相关需配置 Live ProviderliveMode: autoovertimeOptions请求超时提示返回overtime.elapsedTimeovertimeOptions: { interval: 1000, onInterval }八、小结Refine 的useList将列表过滤封装成了声明式的filters数组开发者只需描述「过滤哪些字段、用什么运算符、匹配什么值」即可获得缓存、加载态、错误态、实时订阅等一整套能力。从源码链路看filters会被作为 query key 的一部分参与缓存标识在查询函数中原样传递给getList由具体 data provider 翻译为 API 请求参数。结合 React 状态如示例中的selectfilters的值变化会自动触发重新请求从而实现开箱即用的动态过滤体验。若要深入定制可进一步阅读 Refine 的 data provider 实现如 packages/simple-rest以了解CrudFilters到查询参数的具体映射逻辑。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价