
深入掌握 Refine 的 useMany Hook批量数据获取、实时订阅与进阶用法全解【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine导读useMany是 Refine 中用于批量获取多条记录的核心数据 Hook。本文以 useMany 官方文档 为骨架结合仓库源码 useMany.ts 与测试用例 useMany.spec.tsx系统讲解其工作原理、全部属性resource、ids、queryOptions、meta、通知、实时订阅、加载超时等、返回值与最佳实践。读完本文你将掌握如何在 Refine 应用中高效调用getMany批量拉取数据、处理降级回退、接入实时更新并能在自定义数据提供器中正确实现与消费getMany方法。一、useMany 是什么基于 TanStack Query 的批量查询封装useMany是 Refine 对 TanStack Query 的useQuery的扩展版本支持useQuery的全部特性并增加了 Refine 专属能力。它的定位是一次请求获取多条记录与一次只取一条的useOne、分页列表的useList形成互补。其核心机制在源码 useMany.ts 中清晰可见它使用从Refine传入的dataProvider的getMany方法作为查询函数query function它基于提供的属性生成查询键query key用于缓存数据你可以在 TanStack Query Devtools 中直接查看该查询键。从源码可见查询键由keys().data(...).resource(...).action(many).ids(...).params(...)链式构建这意味着当ids、resource或meta发生变化时查询键随之变化useMany会自动触发新的请求——这正是属性变化即重新拉取的底层原理见 useMany.ts。没有 getMany 时的降级策略一个值得特别注意的设计是如果数据提供器没有实现getMany方法useMany会退而使用getOne方法为每个 id 逐个发起请求。源码中的实现逻辑如下if (getMany) { return getMany({ resource: resource?.name || , ids, meta }); } return handleMultiple( ids.map((id) getOneTQueryFnData({ resource: resource?.name || , id, meta }), ), );见 useMany.ts。文档明确指出这种做法不推荐因为它会为每个 id 产生一次网络请求N 次请求效率远低于单次getMany。因此当你的应用需要批量获取数据时最好在数据提供器中实现真正的getMany方法。二、基础用法一个可交互的完整示例useMany的基础用法非常简单只需提供resource与ids两个属性。下面是文档自带的实时预览示例源文件见 _basic-usage-live-preview.mdimport { useState } from react; import { useMany, HttpError } from refinedev/core; interface IProduct { id: number; name: string; material: string; } const ProductList: React.FC () { const [ids, setIds] useState([1, 2, 3]); const { result, query: { isLoading, isError }, } useManyIProduct, HttpError({ resource: products, ids, }); const products result?.data ?? []; if (isLoading) { return divLoading.../div; } if (isError) { return divSomething went wrong!/div; } return ( div {products.map((product) ( ul key{product.id} li key{product.id} {product.id} - {product.name}{ } button onClick{() setIds((prev) prev.filter((id) id ! product.id)) } remove /button /li /ul ))} button onClick{() { setIds((prev) [...prev, Math.floor(Math.random() * 150) 1]); }} Add new product /button /div ); };这个示例演示了三个关键行为初始批量获取挂载时携带ids: [1, 2, 3]发起一次getMany请求一次性渲染三条产品记录动态移除点击某条记录的 remove 按钮会从ids数组中过滤掉对应 iduseMany检测到ids变化后自动重新请求列表随之收缩动态新增点击 Add new product 会向ids追加一个随机 id触发新的请求并拉取该 id 对应的记录。同时注意useMany的返回结构query对象是 TanStack Query 的查询结果包含isLoading、isError、data等标准字段而result.data是被展开为数组的记录列表。测试用例 useMany.spec.tsx 也验证了result.data一定是一个数组Array.isArray(manyResult.data)为真。三、完整属性Properties详解resource必填resource会作为参数传递给数据提供器的getMany方法。它通常是对应的 API 端点路径但具体如何解析完全取决于你在getMany方法中的实现useMany({ resource: categories, });关于如何自定义getMany方法参见 创建数据提供器教程。当存在多个同名资源时可以传入identifier代替资源的name。注意identifier仅作为资源的主匹配键数据提供器方法内部仍使用在Refine组件中定义的name。详见 Refine 组件的 identifier 说明。ids必填ids用于指定要获取哪些记录同样会传递给getMany方法useMany({ ids: [1, 2, 3], });在源码中ids的类型为BaseKey[]见 useMany.tsBaseKey通常是string | number。请求是否执行取决于enabled: hasIds hasResource见 useMany.ts即只有当ids是数组且resource存在时请求才会真正发起。如果缺少ids或resource源码会通过warnOnce打印出带链接的警告信息帮助开发者快速定位问题见 useMany.ts 与 useMany.ts。dataProviderName当应用配置了多个数据提供器时用该属性指定使用哪一个useMany({ dataProviderName: second-data-provider, });源码默认值为default见 useMany.ts。内部通过pickDataProvider(identifier, dataProviderName, resources)解析出最终要使用的数据提供器并将它写入meta.dataProviderName一并传给getMany见 [useMany.ts](https://link.gitcode.com/i/e42bc478df3747128178a94e655ef672#L135-L139, L170-L173)因此你的getMany实现也能感知到当前使用的是哪个数据提供器。queryOptionsqueryOptions用于向底层useQuery传递额外的配置选项useMany({ queryOptions: { retry: 3, enabled: false, }, });从源码看queryOptions的类型是MakeOptionalUseQueryOptions..., queryKey | queryFn见 useMany.ts即queryKey和queryFn由 Refine 内部接管开发者不能覆盖其余 TanStack Query 选项均可透传。若你手动把enabled设为true即使缺少ids或resource请求也会被强制触发源码中的manuallyEnabled分支就是为这种场景设计的。更多选项参考 TanStack Query 的 useQuery 文档。metameta是一个特殊属性用于向数据提供器方法传递额外信息典型用途包括针对特定场景定制数据提供器行为使用普通 JavaScript 对象JSON生成 GraphQL 查询。下面的示例通过meta向getMany方法传递自定义请求头import { stringify } from query-string; useMany({ // highlight-start meta: { headers: { x-meta-data: true }, }, // highlight-end }); const myDataProvider { //... getMany: async ({ resource, ids, // highlight-next-line meta, }) { // highlight-next-line const headers meta?.headers ?? {}; const url ${apiUrl}/${resource}?${stringify({ id: ids })}; //... //... // highlight-next-line const { data } await httpClient.get(${url}, { headers }); return { data, }; }, //... };在源码层面meta会经过useMeta()与资源级元数据合并得到combinedMeta见 useMany.ts并在调用getMany时连同查询上下文prepareQueryContext其中包含分页、排序、过滤等查询参数一起传入const meta { ...combinedMeta, ...prepareQueryContext(context as any), };见 useMany.ts。测试用例 useMany.spec.tsx 专门验证了 hook 参数中的meta会被完整透传给数据提供器。更多说明参见 General Concepts 文档的 meta 概念。successNotification / errorNotification这两个属性用于自定义成功与失败时的通知需要配合NotificationProvider使用useMany({ successNotification: (data, ids, resource) { return { message: ${data.title} Successfully fetched., description: Success with no errors, type: success, }; }, }); useMany({ errorNotification: (data, ids, resource) { return { message: Something went wrong when getting ${data.id}, description: Error, type: error, }; }, });对应实现细节成功通知请求成功后若successNotification是函数则以(queryResponse.data, ids, identifier)为参数调用再交给handleNotification见 useMany.ts失败通知请求失败时先调用checkError(error)触发鉴权错误处理如登出跳转再发送错误通知默认消息格式为Error (status code: ${statusCode})并带有${ids[0]}-${identifier}-getMany-notification这样的去重键防止同一请求反复弹错见 useMany.ts。liveMode / onLiveEvent / liveParams实时更新这三个属性依赖LiveProvider。useMany挂载时会调用liveProvider的subscribe方法订阅指定频道从而实现数据实时刷新。liveMode决定收到相关实时事件时是否自动更新数据取值为auto自动或manual手动useMany({ liveMode: auto, });onLiveEvent订阅到达新事件时执行的回调useMany({ onLiveEvent: (event) { console.log(event); }, });liveParams透传给liveProvider的subscribe方法的额外参数。源码中的订阅逻辑位于 useMany.ts订阅频道为resources/${resource?.name ?? }订阅类型为*并在params中携带ids、meta、subscriptionType: useMany以及你传入的liveParams。实时能力仅在配置了 Live Provider 时可用。overtimeOptions加载超时检测当你希望请求耗时过长时展示加载提示可以使用overtimeOptions。interval是检测间隔毫秒onInterval是每个间隔触发的回调const { overtime } useMany({ //... overtimeOptions: { interval: 1000, onInterval(elapsedInterval) { console.log(elapsedInterval); }, }, }); console.log(overtime.elapsedTime); // undefined, 1000, 2000, 3000 4000, ... // 使用示例超过 4 秒就给出提示 { elapsedTime 4000 divthis takes a bit longer than expected/div; }源码中该能力由useLoadingOvertimeHook 提供其内部以queryResponse.isFetching作为正在加载的判定依据请求完成后elapsedTime恢复为undefined见 useMany.ts。四、返回值Return ValuesuseMany返回 TanStack QueryuseQuery的全部返回值并额外提供result与overtimeconst { query, result, overtime } useMany();query类型为QueryObserverResult{ data: TData[]; error: TError }包含isLoading、isError、isSuccess、data、error等标准字段可直接与 TanStack Query 生态如 Devtools、isFetching配合resultRefine 提供的便捷结构result.data是TData[]数组。源码中当查询未完成时它返回Object.freeze([])冻结的空数组保证类型安全与不变性见 [useMany.ts](https://link.gitcode.com/i/e42bc478df3747128178a94e655ef672#L88, L261-L267)overtime{ elapsedTime?: number }配合上面的overtimeOptions使用。五、TypeScript 泛型参数useMany支持三个泛型参数用于获得完整的类型推导参数说明类型默认值TQueryFnData查询函数返回的结果数据类型继承自BaseRecordBaseRecordBaseRecordTError自定义错误对象继承自HttpErrorHttpErrorHttpErrorTDataselect函数返回的数据类型继承自BaseRecord未指定时默认为TQueryFnDataBaseRecordTQueryFnData典型用法useManyIProduct, HttpError({ resource: products, ids, });六、源码视角useMany 的完整执行链路综合 useMany.ts 的完整实现一次useMany调用的执行链路可以归纳为以下几步资源解析useResourceParams根据传入的resource解析出最终资源名与identifier数据提供器选择useDataProviderpickDataProvider确定使用哪个数据提供器同时解析出getMany与getOne两个方法实时订阅注册useResourceSubscription以resources/${resource}为频道发起订阅需 Live Provider发起查询useQuery使用链式构建的查询键queryFn优先调用getMany缺失时回退为逐 id 调用getOne请求仅在ids有效且resource存在时启用通知与错误处理成功后触发successNotification失败后先走useOnError鉴权错误处理再触发errorNotification超时检测useLoadingOvertime监听isFetching输出overtime.elapsedTime。这条链路在 useMany.spec.tsx 的测试中被逐一验证包括与 REST JSON 服务器集成返回两条数据、result.data的数组形态、meta的透传等场景可作为你理解行为与编写自定义数据提供器时的参照。七、最佳实践小结优先实现getMany只要业务中存在批量取数就在数据提供器中实现getMany避免useMany降级为 N 次getOne请求善用ids的响应式把ids放进useState如本文示例增删 id 即可驱动自动重新请求无需手动调用刷新用queryOptions.enabled控制时机如页面尚未拿到 id 列表时用enabled: false阻止请求待数据就绪再置为true用meta透传业务上下文请求头、GraphQL 片段等与单次请求绑定的信息放入meta保持数据提供器方法签名稳定实时场景打开liveMode: auto配合 Live Provider 让表格/详情页在多端协同编辑场景下自动更新超时提示用overtimeOptions以interval细粒度控制加载过久的提示时机提升弱网环境下的用户体验。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考