news 2026/9/11 1:32:17

Refine 的 useCustom Hook 完全指南:自定义查询、配置参数与实现原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Refine 的 useCustom Hook 完全指南:自定义查询、配置参数与实现原理

Refine 的 useCustom Hook 完全指南:自定义查询、配置参数与实现原理

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

useCustom是 Refine v5 中用于发送自定义查询请求的数据 Hook,它基于 TanStack Query 的useQuery扩展而来,将urlmethodconfig等参数透传给数据提供者(dataProvider)的custom方法。本文以仓库内官方文档与源码为依据,系统讲解useCustom的全部属性、返回值与底层实现机制,帮助你对接自定义 API 端点、执行唯一性校验、调用报表接口等无法用标准 CRUD Hook 覆盖的场景,并掌握查询失效(invalidate)与超时提示的正确姿势。

useCustom 是什么

useCustom是 Refine 中面向“自定义请求”的查询 Hook。它本质上是 TanStack QueryuseQuery的扩展版本:不仅完整继承useQuery的全部能力(缓存、重试、轮询、select转换等),还额外提供了 Refine 生态特有的能力——通知(NotificationProvider)、鉴权错误回调(onError)、meta 合并、加载超时(overtime)等。

从源码可以看到它的核心职责,见 packages/core/src/hooks/data/useCustom.ts:

if (custom) { const queryResponse = useQuery<...>({ queryKey: keys() .data(dataProviderName) .mutation("custom") .params({ method, url, ...config, ...(preferredMeta || {}) }) .get(), queryFn: (context) => custom<TQueryFnData>({ url, method, ...config, meta: { ...combinedMeta, ...prepareQueryContext(context as any) }, }), ...queryOptions, }); }

也就是说,useCustom查询函数(queryFn)就是 dataProvider 上的custom方法。只要在传给<Refine>的 dataProvider 上实现了customuseCustom就能正常工作;如果 dataProvider 没有实现custom,Hook 会直接抛出错误Not implemented custom on data provider.(这一点在测试用例 packages/core/src/hooks/data/useCustom.spec.tsx 中有明确验证)。

何时不该使用 useCustom

:::caution 使用场景警告useCustom不应用于资源的增、删、改操作。创建、更新、删除请分别使用 useCreate、useUpdate、useDelete。 :::

原因在于:useCustom与其他数据 Hook 不同,它不会自动失效(invalidate)相关查询,因此也不会主动刷新应用状态。如果你需要自定义的是“变更类”请求,请改用 useCustomMutation Hook。

基本用法

useCustom要求必传urlmethod两个属性,它们会作为参数传给 dataProvider 的custom方法;当这些属性发生变化时,Hook 会触发一次新的请求(得益于 TanStack Query 的 queryKey 机制)。

典型用法如下,先通过useApiUrl拿到当前 dataProvider 的 API 基础地址,再拼接自定义端点:

import { useCustom, useApiUrl } from "@refinedev/core"; interface PostUniqueCheckResponse { isAvailable: boolean; } const apiUrl = useApiUrl(); const { query } = useCustom<PostUniqueCheckResponse>({ url: `${apiUrl}/posts-unique-check`, method: "get", config: { headers: { "x-custom-header": "foo-bar", }, }, });

useApiUrl内部会读取当前资源对应的 dataProvider(可通过参数指定名称),并调用其getApiUrl方法返回基础 URL,见 packages/core/src/hooks/data/useApiUrl.ts。

Properties 详解

url(必填)

url会被直接传给 dataProvider 的custom方法,通常用于指定请求的端点地址:

useCustom({ url: "www.example.com/api/get-products", });

method(必填)

method指定 HTTP 方法。在源码中,method的类型被严格限制为以下七种之一,见 packages/core/src/hooks/data/useCustom.ts:

method: "get" | "delete" | "head" | "options" | "post" | "put" | "patch";
useCustom({ method: "get", });

config.headers

用于指定请求头,会原样透传给custom方法:

useCustom({ config: { headers: { "x-custom-header": "foo-bar", }, }, });

config.query

用于指定查询参数(query string):

useCustom({ config: { query: { title: "Foo bar", }, }, });

config.payload

用于指定请求体(body)。例如调用postput类自定义端点时携带数据:

useCustom({ config: { payload: { title: "Foo bar", }, }, });

config.sorters

用于发送排序参数,字段结构与getList的排序一致:

useCustom({ config: { sorters: [ { field: "title", order: "asc", }, ], }, });

config.filters

用于发送过滤参数,支持 Refine 的条件运算符(如contains):

useCustom({ config: { filters: [ { field: "title", operator: "contains", value: "Foo", }, ], }, });

在源码中,config的完整类型为UseCustomConfig,包含sortersfiltersquerypayloadheaders五个可选字段,见 packages/core/src/hooks/data/useCustom.ts。

queryOptions

queryOptions用于向底层的useQuery透传额外选项,例如调整重试次数、禁用自动请求等。它与 TanStack QueryuseQuery的选项保持一致:

useCustom({ queryOptions: { retry: 3, enabled: false, }, });

注意一个细节:Refine 在内部已经为你生成了 queryKey,因此queryOptions中的queryKeyqueryFn在类型上被设计为可选(以便向后兼容),见 packages/core/src/hooks/data/useCustom.ts。当你主动传入queryKey时,它会覆盖默认的自动生成的 key——这一点在测试用例“with custom query key”中有验证,见 packages/core/src/hooks/data/useCustom.spec.tsx。

meta

meta是一个特殊属性,用于向 dataProvider 方法传递额外信息,常见用途有:

  • 针对特定用例定制 dataProvider 方法的行为;
  • 用纯 JavaScript 对象(JSON)生成 GraphQL 查询。

在下面的示例中,meta作为参数传入 dataProvider 的custom方法:

useCustom({ meta: { foo: "bar", }, }); const myDataProvider = { //... custom: async ({ url, method, sort, filters, payload, query, headers, meta, }) => { const foo = meta?.foo; console.log(foo); // "bar" //... }, //... };

从实现上看,meta还会与当前路由参数、资源级 meta 合并:源码中通过getMeta合并显式传入的meta与资源上下文 meta,并在 queryFn 里与 TanStack Query 的查询上下文一起传给custom,见 packages/core/src/hooks/data/useCustom.ts。测试用例验证了meta会与路由参数baz: "qux"合并后传给custom方法,见 packages/core/src/hooks/data/useCustom.spec.tsx。

dataProviderName

当应用配置了多个 dataProvider 时,用dataProviderName指定本次请求使用哪一个:

useCustom({ dataProviderName: "second-data-provider", });

successNotification

成功通知定制。需要先配置 NotificationProvider此属性才生效。请求成功且useCustom调用 NotificationProvider 的open函数时,会使用这里返回的配置:

useCustom({ successNotification: (data, values) => { return { message: `${data.title} Successfully fetched.`, description: "Success with no errors", type: "success", }; }, });

源码中,成功通知的处理逻辑位于 packages/core/src/hooks/data/useCustom.ts:当queryResponse.isSuccess且存在数据时,如果successNotification是函数,则以(数据、合并后的 config 与 meta)为参数调用它;返回false则可静默跳过通知(测试用例“should not call open from notification provider on return false”验证了这一点)。

errorNotification

失败通知定制,同样依赖 NotificationProvider:

useCustom({ errorNotification: (data, values) => { return { message: `Something went wrong when getting ${data.id}`, description: "Error", type: "error", }; }, });

需要留意的是:即使你没有传errorNotification,Refine 仍会为失败请求弹出一条默认错误通知(key 为${method}-notification,例如get-notification,message 为Error (status code: ...)),相关逻辑见 packages/core/src/hooks/data/useCustom.ts。测试用例 packages/core/src/hooks/data/useCustom.spec.tsx 也断言了默认错误通知的内容。

overtimeOptions

用于请求加载超时的场景——当请求耗时过长时展示加载提示。interval是时间间隔(毫秒),onInterval会在每个间隔触发一次:

const { overtime } = useCustom({ //... overtimeOptions: { interval: 1000, onInterval(elapsedInterval) { console.log(elapsedInterval); }, }, }); console.log(overtime.elapsedTime); // undefined, 1000, 2000, 3000 4000, ... // You can use it like this: { elapsedTime >= 4000 && <div>this takes a bit longer than expected</div>; }

返回的overtime.elapsedTime表示已耗时(毫秒),请求完成时变为undefined。测试用例验证了intervalonInterval的行为:请求挂起期间elapsedTime持续累加,请求完成后重置为undefined,见 packages/core/src/hooks/data/useCustom.spec.tsx。

返回值

useCustom返回一个对象,源码定义见 packages/core/src/hooks/data/useCustom.ts:

名称说明类型
queryTanStack Query 的查询结果对象QueryObserverResult<CustomResponse<TData>, TError>
result便捷取数对象,result.data即响应数据{ data: CustomResponse<TData>["data"] }
overtime加载超时信息,{ elapsedTime?: number }{ elapsedTime?: number }

其中CustomResponse的定义为{ data: TData },见 packages/core/src/contexts/data/types.ts。因此你可以用query.data?.dataresult.data两种方式读取真正的响应数据。

类型参数(Type Parameters)

属性说明类型默认值
TQueryFnData查询函数返回的数据类型,继承自BaseRecordBaseRecordBaseRecord
TError自定义错误对象,继承自HttpErrorHttpErrorHttpError
TQuery查询参数的类型TQueryunknown
TPayload请求体参数的类型TPayloadunknown
TDataselect函数返回的数据类型,继承自BaseRecord;未指定时默认取TQueryFnDataBaseRecordTQueryFnData

说明:BaseRecordHttpError均为 Refine 核心接口,定义可参考 packages/core/src/contexts/data/types.ts。

源码视角:useCustom 的底层执行链路

结合源码与测试,可以梳理出useCustom的完整执行链路:

  1. 取 dataProvider:通过useDataProviderdataProviderName(缺省为当前资源关联的 provider)解析出 provider,并取出其custom方法;
  2. 生成 queryKey:Refine 使用keys().data(dataProviderName).mutation("custom").params({ method, url, ...config, ...meta }).get()自动构建稳定的查询键——这意味着只要urlmethodconfigmeta变化,queryKey 就会变化并触发重新请求
  3. 执行查询:queryFn 调用custom,并额外传入合并了 meta 与查询上下文的信息;
  4. 通知与鉴权:成功时触发成功通知;失败时先调用鉴权提供者的onError/checkError处理(401 等场景),再触发错误通知;
  5. 超时跟踪:通过useLoadingOvertime基于queryResponse.isFetching驱动overtime.elapsedTime
  6. 无 custom 时抛错:如果 dataProvider 未实现custom,抛出Not implemented custom on data provider.

与之对应的参数契约是CustomParams,见 packages/core/src/contexts/data/types.ts:它定义了urlmethodsorters?filters?payload?query?headers?meta?等字段,这也是实现自定义 dataProvider 时custom方法的标准入参。

真实数据提供者如何实现 custom:以 simple-rest 为例

以仓库内置的@refinedev/simple-rest为例,其custom实现(见 packages/simple-rest/src/provider.ts)展示了sortersfiltersquery如何被拼接为查询字符串:排序会转换为_sort/_order参数,过滤会通过generateFilter生成查询串,query直接 stringify 追加到 URL,随后按method分发到 axios 请求。这解释了useCustom传入的config在真实 REST 场景下的落点——你可以基于此模式编写自己的custom实现,也可以直接使用 simple-rest 获得开箱即用的自定义请求支持。

FAQ:如何使自定义查询失效(invalidate)

由于useCustom不会自动失效查询,当你需要主动刷新数据(例如某个自定义查询的依赖数据被更新后)时,可以使用 TanStack Query 的useQueryClient提供的invalidateQueries方法:

import { useQueryClient } from "@tanstack/react-query"; const queryClient = useQueryClient(); queryClient.invalidateQueries(["custom-key"]);

请注意,你需要知道该查询的 queryKey才能使其失效。如果你不清楚默认生成的 key,可以通过queryOptions.queryKeyuseCustom显式指定一个稳定的 key:

import { useCustom } from "@refinedev/core"; useCustom({ queryOptions: { queryKey: ["custom-key"], }, });

之后无论何时调用invalidateQueries(["custom-key"]),都能精准命中并刷新该自定义查询。

小结

  • useCustom是 Refine 中对接自定义端点的首选查询 Hook,完整继承 TanStack QueryuseQuery的能力,并叠加 Refine 的通知、鉴权、meta 与超时机制。
  • 全部请求参数通过urlmethodconfig(headers/query/payload/sorters/filters)与meta传递给 dataProvider 的custom方法,参数变化会自动触发重新请求。
  • 增删改场景请使用useCreate/useUpdate/useDelete,需要自定义变更请求时使用useCustomMutation
  • 手动刷新自定义查询时,通过queryOptions.queryKey固定 key,再配合queryClient.invalidateQueries精准失效。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 1:31:25

机械臂PD控制闭环实现:MATLAB建模到PLC部署全链路

简介&#xff1a;本资源是一套面向机器人控制初学者与自动化专业学生的机械臂PD控制MATLAB仿真教学包&#xff0c;聚焦双连杆机械臂建模、PD控制器设计与Simulink闭环仿真全流程实践。资源完整覆盖动力学建模&#xff08;牛顿-欧拉法&#xff09;、关节级PD参数整定、轨迹跟踪效…

作者头像 李华
网站建设 2026/9/11 1:30:59

人大金仓数据库定时备份实战:从脚本设计到恢复演练的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 1:30:50

位图整数:多选项存储的高效方案与工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 1:30:20

零门槛上手PCSX2:从下载到第一帧画面只要5分钟

零门槛上手PCSX2&#xff1a;从下载到第一帧画面只要5分钟 【免费下载链接】pcsx2 PCSX2 - The Playstation 2 Emulator 项目地址: https://gitcode.com/GitHub_Trending/pc/pcsx2 你手头还有一台老PS2主机&#xff0c;或一箱当年的游戏光盘&#xff1f;用PCSX2这款PS2模…

作者头像 李华
网站建设 2026/9/11 1:28:38

App云测试平台核心价值与实施策略全解析

1. 为什么App云测试平台成为行业刚需&#xff1f;在移动互联网爆发式增长的十年间&#xff0c;App质量已成为决定产品生死的关键因素。我亲眼见证过多个团队因测试覆盖率不足导致的惨痛案例&#xff1a;某金融类App因未检测到特定机型上的支付界面错位&#xff0c;上线首日损失…

作者头像 李华