
Refine useOne 钩子详解基于 TanStack Query 的单条记录查询、实时订阅与源码级实现剖析【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本文围绕 RefineReact 内部工具 / 管理面板元框架核心包refinedev/core提供的useOne数据钩子展开先完整继承官方 API 文档中关于useOne的参数说明与用法resource、id、dataProviderName、queryOptions、metaData、通知回调、liveMode 等再结合当前仓库中的真实源码 useOne 实现、配套测试 useOne.spec.tsx 与实时订阅实现 useResourceSubscription剖析 query key 生成规则、enabled 判定逻辑、通知触发链路等底层细节帮助读者既会用、也看得懂它在 TanStack Query 之上的封装原理。一、useOne 是什么useOne是 TanStack QueryuseQuery的扩展版本见 useOne 文档。它支持useQuery的全部能力并在此基础上增加了两项 Refine 特有的能力以dataProvider的getOne方法作为 query 函数这个dataProvider是通过Refine组件传入的。调用方不需要手写 HTTP 请求只需声明要查哪个资源、哪一条记录基于查询参数自动生成的 query key 缓存query key 由传入的属性生成可直接在 TanStack Query Devtools 中查看天然获得缓存、去重与失效管理能力。它适用于“从 API 获取单条记录”的场景返回查询数据以及用于控制查询的一组函数。从当前仓库源码 useOne.ts 的类型定义看UseOneProps由以下部分组合而成TanStack Query 的UseQueryOptions排除queryKey/queryFn后可选覆盖、MetaQuery元数据、dataProviderName、SuccessErrorNotification成功/失败通知、LiveModeProps实时模式以及UseLoadingOvertimeOptionsProps加载超时计时。这一组合式类型正是理解本文后续所有参数的骨架。二、基础用法Basic UsageuseOne要求传入resource和id两个属性它们会被作为参数传给dataProvider的getOne方法。当这些属性变化时useOne会触发一次新的请求——这正是它作为查询型钩子的核心行为参数即依赖依赖变则重取。官方文档附带的实时预览示例见 basic-usage-live-preview.md是一个完整可运行的产品详情组件import { useState } from react; import { useOne, HttpError } from pankod/refine-core; interface IProduct { id: number; name: string; material: string; } const ProductList: React.FC () { const [id, setId] useState(1); const { data, isLoading, isError } useOneIProduct, HttpError({ resource: products, id, }); const product data?.data; if (isLoading) { return divLoading.../div; } if (isError) { return divSomething went wrong!/div; } return ( div h3Product Details/h3 pid: {product?.id}/p pname: {product?.name}/p pmaterial: {product?.material}/p br / button onClick{() setId((prev) prev - 1)} disabled{id 1} {} Prev Product /button button onClick{() setId((prev) prev 1)} Next Product {} /button /div ); };要点useOne的泛型参数IProduct, HttpError用于声明返回数据与错误对象的类型获得完整的 TS 类型推导点击 Prev/Next 按钮修改id后useOne会因id变化自动发起新的getOne请求而前一次请求的结果已缓存在前一个 query key 下可瞬间切回。三、属性Properties详解resource必填作为参数传给dataProvider的getOne方法。该参数通常被用作 API 端点路径的一部分具体取决于你在getOne中如何处理resourceuseOne({ resource: categories, });id必填同样传给getOne方法用于确定要获取哪一条记录useOne({ id: 123, });结合源码可以看到resource和id不仅决定请求内容还直接参与两个关键逻辑query key 的一部分见下文“query key 生成”一节enabled 判定useOne.ts 中默认enabled为resource与id均已定义时为true——也就是说id未就绪例如路由参数还在解析中时查询不会发起这是 Refine 内建的一个防抖/防误查机制。dataProviderName当你注册了多个dataProvider时可以通过dataProviderName指定使用哪一个适用于“不同资源走不同数据后端”的场景useOne({ dataProviderName: second-data-provider, });源码中该选择由 pickDataProvider 完成且选择的 provider 名会同时被写入 query key 与 meta 中。测试用例 useOne.spec.tsxshould select correct dataProviderName验证了当资源通过meta.dataProviderName声明使用fooprovider 时只有fooprovider 的getOne被调用defaultprovider 的getOne未被调用。queryOptions用于向底层useQuery传递额外选项如重试次数、是否启用等useOne({ queryOptions: { retry: 3, enabled: false, }, });一个容易被忽略的细节当前源码中queryOptions允许覆盖queryKey和queryFn。类型定义见 useOne.ts。测试用例证实了这两条覆盖路径传入queryOptions.queryKey: [foo, bar]后查询缓存中确实以[foo, bar]为 key 存在且该 key 会透传进getOne的metaspec 对应断言传入自定义queryOptions.queryFn后dataProvider.getOne完全不会被调用spec 对应断言。另外queryOptions.enabled: false不仅会暂停查询还会阻止实时订阅的建立——测试用例should not subscribe if queryOptions.enabled is false断言了此时liveProvider.subscribe未被调用useOne.spec.tsx。metaData元数据metaData当前源码中已更名为meta见 useOne.ts有两个用途向 data provider 方法传递额外信息用纯 JS 对象JSON生成 GraphQL 查询。官方文档示例展示了通过metaData传递自定义请求头的完整链路useOne({ // highlight-start metaData: { headers: { x-meta-data: true }, }, // highlight-end }); const myDataProvider { //... getOne: async ({ resource, id, // highlight-next-line metaData, }) { // highlight-next-line const headers metaData?.headers ?? {}; const url ${apiUrl}/${resource}/${id}; //... //... // highlight-next-line const { data } await httpClient.get(${url}, { headers }); return { data, }; }, //... };从源码看传给getOne的meta是合并而来的useMeta()会把Refine中资源级声明的 meta 与钩子参数中的 meta 合并useOne.ts随后再叠加查询上下文信息prepareQueryContext。测试用例should get correct meta of related resource验证了资源级 meta如resources配置中的meta: { foo: bar }会自动出现在getOne收到的meta中useOne.spec.tsx。这意味着你既可以在应用启动时按资源声明默认元数据也可以在单次调用时用钩子参数覆盖。successNotification需要NotificationProvider支持。数据成功获取后useOne会调用NotificationProvider的open函数弹出成功通知该属性用于自定义通知内容useOne({ successNotification: (data, values, resource) { return { message: ${data.title} Successfully fetched., description: Success with no errors, type: success, }; }, });源码实现useOne.ts中successNotification既可以是函数参数为data、{ id, ...meta }、identifier也可以是静态配置对象最终统一交给handleNotification处理返回false可抑制通知——测试用例验证了successNotification: () false时open调用次数为 0useOne.spec.tsx。errorNotification需要NotificationProvider支持。获取失败时调用open弹出错误通知useOne({ errorNotification: (data, values, resource) { return { message: Something went wrong when getting ${data.id}, description: Error, type: error, }; }, });从源码看useOne.ts默认错误通知的 message 为Error (status code: ${statusCode})支持 i18n 翻译 keynotifications.errordescription 为错误对象的message通知 key 为${id}-${identifier}-getOne-notification。此外错误还会被上报给useOnErroruseOne.ts进而调用authProvider.onError或旧版checkError——典型场景是 401 时自动登出。测试用例should call onError from the auth provider on error验证了这一链路useOne.spec.tsx。liveMode需要LiveProvider支持。决定收到相关 live 事件时是自动更新数据auto还是不自动更新manual用于在应用中实时展示数据useOne({ liveMode: auto, });onLiveEvent需要LiveProvider支持。订阅收到新事件时执行的回调useOne({ onLiveEvent: (event) { console.log(event); }, });liveParams需要LiveProvider支持。传给liveProvider.subscribe方法的附加参数。四、实时Realtime机制源码级拆解该功能仅在配置了LiveProvider时可用。useOne挂载时会携带channel、resource等参数调用liveProvider的subscribe方法从而订阅该条记录的实时变更。这一行为的完整实现可以定位到 useOne.ts 中的useResourceSubscription调用channel:resources/${resource?.name}如resources/poststypes:[*]监听所有事件类型params:{ ids: [id], id, meta, subscriptionType: useOne, ...liveParams }。而 useResourceSubscription 内部的关键逻辑是liveMode 优先级钩子参数上的liveMode优先于Refine全局liveModeliveModeFromProp ?? liveModeFromContext。测试用例验证了全局liveMode: off时不订阅但钩子参数显式传liveMode: auto时仍会订阅auto 模式的自动失效事件回调中若liveMode auto会调用invalidate对该资源所有相关查询做refetchType: active的失效刷新从而让页面数据自动更新manual模式则只把事件交给onLiveEvent回调由开发者自行决定如何刷新卸载即退订useEffect的清理函数会调用liveProvider?.unsubscribe(subscription)。测试用例unsubscribe call on unmount断言了组件卸载时unsubscribe恰好被调用一次useOne.spec.tsx。订阅参数与断言细节均可在 useOne.spec.tsx 的useResourceSubscription系列用例中逐条对照channel: resources/posts、ids: [1]、subscriptionType: useOne、meta.dataProviderName等均被精确验证。五、query key 是怎么生成的query key 的生成链在 useOne.tsqueryKey: keys() .data(pickedDataProvider) // 选定的 dataProvider 名 .resource(identifier ?? ) // 资源的 identifier .action(one) // 固定动作名 one .id(id ?? ) // 记录 id .params({ ...(preferredMeta || {}) }) // 元数据 .get(),测试用例给出了一个真实的 key 形态资源posts、identifier 为featured-posts、id 为1使用默认 provider[data, default, featured-posts, one, 1, params对象]见 useOne.spec.tsx。这一结构解释了官方文档中两条行为描述“resource/id变化会触发新请求”——因为它们都在 key 中“同一查询命中同一缓存”——同dataProviderName identifier id meta的useOne共享同一条缓存记录这正是useOne在列表页→详情页之间秒切体验的来源。值得注意的是 key 中使用的是identifier而非name当同一name注册了多个带不同identifier的资源时它们拥有彼此独立的缓存空间测试用例should create queryKey with identifier专门验证了这一点。六、返回值的版本演进v3 文档口径返回 TanStack QueryuseQuery的标准结果对象直接解构即可得到data、isLoading、isError等如第二节示例中的用法。当前仓库源码口径refinedev/core5.x见 package.json 中版本 5.0.12返回结构调整为三段式见 UseOneReturnType{ query: QueryObserverResultGetOneResponseTData, TError, // 完整的 TanStack Query 结果 result: TData | undefined, // 已解包的 data.data overtime: { elapsedTime }, // 加载超时计时v5 新增 }对应的测试断言为result.current.query.isSuccess/result.current.resultuseOne.spec.tsx。此外 v5 还引入了overtimeOptionsinterval、onInterval可追踪一次请求的耗时并在超时前周期性回调测试用例works correctly with interval and onInterval params验证了elapsedTime的累计与请求完成后的复位useOne.spec.tsx。适用前提说明本文基于仓库documentation/versioned_docs/version-3.xx.xx下的 useOne 文档并对照当前packages/core源码5.0.12进行佐证。跨版本使用时请注意metaData→meta的更名、返回值结构从“直接展开 useQuery 结果”到{ query, result, overtime }的调整、以及pankod/refine-core→refinedev/core的包名迁移。阅读示例代码时请以所安装的主版本号为准。七、类型参数属性说明类型默认值TData查询返回的数据继承BaseRecordBaseRecordBaseRecordTError自定义错误对象继承HttpErrorHttpErrorHttpError当前源码中泛型实际为三元组TQueryFnData, TError, TDatauseOne.tsTData默认等于TQueryFnData用于配合queryOptions.select对查询结果做映射。八、参考索引文档入口useOne 官方 API 文档钩子实现useOne.ts测试用例useOne.spec.tsx实时订阅实现useResourceSubscription基础用法预览basic-usage-live-preview.md【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考