ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Refine v5 的 useImport Hook 完全指南:从 CSV 批量导入到数据 Provider 的底层实现

Refine v5 的 useImport Hook 完全指南:从 CSV 批量导入到数据 Provider 的底层实现 Refine v5 的 useImport Hook 完全指南从 CSV 批量导入到数据 Provider 的底层实现【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refineuseImport是 Refine 的 Ant Design 集成refinedev/antd中用于从 CSV 文件批量导入数据到管理后台的核心 Hook。它逐行或按批次调用数据提供者的create/createMany方法内部借助 Papa Parse 完成 CSV 解析并返回与 Ant DesignUpload、Button组件直接兼容的属性。阅读本文后你将掌握 useImport 的全部配置项、返回值、关系数据映射mapData实战技巧以及从refinedev/core到refinedev/antd的完整调用链与底层实现原理。本文以 documentation/docs/ui-integrations/ant-design/hooks/use-import/index.md 为骨架并结合 packages/antd/src/hooks/import/index.tsx、packages/core/src/hooks/import/index.tsx 等仓库源码展开。useImport 是什么useImportHook 允许你从一个CSV文件导入数据。对于文件中的每一行它会根据你的配置调用数据提供者的create或createMany方法。内部使用 Papa Parse 解析文件内容返回值与 Ant Design 的Upload与Button组件完全兼容。从包结构看refinedev/antd的useImport是对refinedev/core中同名 Hook 的扩展封装antd 封装位于 packages/antd/src/hooks/import/index.tsxcore 实现位于 packages/core/src/hooks/import/index.tsx。这意味着 core 版本的所有能力你都可以使用antd 版本在其之上额外提供了与 Ant Design 组件体系对接的uploadProps/buttonProps默认的导入进度通知基于 Ant Design 的notification与Progress组件见 packages/antd/src/hooks/import/index.tsx。也就是说refinedev/antd版本面向的是接入 Ant Design 界面的场景如果你在无 UI 依赖headless的环境下工作可以直接使用 core 版本。基础用法配合 ImportButton 使用推荐最简单的用法是配合refinedev/antd提供的ImportButton组件import { ImportButton, useImport } from refinedev/antd; export const PostList: React.FC () { const importProps useImport(); return ImportButton {...importProps}Import/ImportButton; };ImportButton内部用一个Upload包裹Button并内置了导入图标ImportOutlined、data-testid与 className见 packages/antd/src/components/buttons/import/index.tsx。它的属性类型ImportButtonProps要求分别传入uploadProps和buttonProps见 packages/antd/src/components/buttons/types.ts这恰好与useImport的返回值一一对应因此可以直接展开传递。更多关于ImportButton的细节可参考 documentation/docs/ui-integrations/ant-design/components/buttons/import-button/index.md。不使用 ImportButton 的自定义用法如果你需要更自由的定制可以只取uploadProps和buttonProps两个属性自行组合 Ant Design 的Upload与Buttonimport { useImport } from refinedev/antd; import { Upload, Button } from antd; export const PostList: React.FC () { const { buttonProps, uploadProps } useImport(); return ( Upload {...uploadProps} Button {...buttonProps}Import/Button /Upload ); };uploadProps已经替你处理了文件选择、禁用自动上传、隐藏上传列表、限定.csv扩展名等细节见下文返回值的内部细节一节因此这里只需要把属性展开即可。Properties 配置项详解resourceresource决定数据提供者的create/createMany方法将被调用在哪个资源上。默认情况下它从当前 URL 路由推断资源名antd 版本通过useResourceParams解析见 packages/antd/src/hooks/import/index.tsx。useImport({ resource: posts, });如果你有多个同名资源可以传入identifier而非name。identifier只作为资源匹配的主键而数据提供者方法仍使用Refine/组件中定义的name。从实现上看antd 版本会优先取resource?.identifier ?? resource?.name传给 core 版本见 packages/antd/src/hooks/import/index.tsx。关于identifier的完整说明参考 documentation/docs/core/refine-component/index.md 中的 identifier 一节。mapData在将数据发送给数据提供者方法之前如果你想对解析出的每一行做变换可以使用mapDatauseImport({ mapData: (data) ({ ...data, category: { id: data.categoryId, }, }), });从源码看mapData的默认值是恒等函数(item) item as unknown as TVariables见 packages/core/src/hooks/import/index.tsx。它的完整签名是MapDataFnTItem, TVariables调用时会被传入(item, index, array)三个参数见 packages/core/src/definitions/helpers/importCSVMapper/index.ts因此你还可以基于行号或整表数据进行变换。paparseOptions你可以把任意 Papa Parse 配置项 传给paparseOptionsuseImport({ paparseOptions: { header: true, }, });在 core 实现中这些选项会原样展开传给papaparse.parse(file, { complete, ...paparseOptions })见 packages/core/src/hooks/import/index.tsx。其类型为papaparse.ParseConfig见 packages/core/src/hooks/import/index.tsx常见的用法还包括自定义分隔符delimiter、跳过空行skipEmptyLines、错误处理回调error等。batchSizebatchSize控制请求的批处理方式是理解 useImport 行为的关键配置batchSize 1对文件中每一行调用一次create方法batchSize 1将数据按该大小切成多个块对每个块调用一次createMany方法默认值为Number.MAX_SAFE_INTEGER见 packages/core/src/hooks/import/index.tsx即默认一次性把全部行塞进一次createMany调用。useImport({ batchSize: 1, });在 core 实现中batchSize 1时会对每一行构造一个create.mutateAsync调用然后通过sequentialPromises顺序执行逐个发起请求并累计进度否则使用lodash/chunk切块后同样以顺序方式逐批调用createMany.mutateAsync见 packages/core/src/hooks/import/index.tsx。注意顺序执行意味着大批量文件不会并发打爆后端但耗时也会线性增长。如果batchSize 1你的数据提供者必须实现createMany这一点在 core 的类型注释中也有明确说明见 packages/core/src/hooks/import/index.tsx。此外batchSize取undefined时行为与默认一致走createMany全量一次这一点被测试用例专门覆盖见 packages/core/src/hooks/import/index.spec.tsx。onFinish在导入流程全部结束后触发用于处理成功与失败的结果。回调参数是一个包含succeeded与errored两个数组的对象分别存放成功与失败请求的响应useImport({ onFinish: (result) { // success requests response result.succeeded.forEach((item) { console.log(item); }); // failed requests response result.errored.forEach((item) { console.log(item); }); }, });对应类型定义在 packages/core/src/hooks/import/index.tsxsucceeded: ImportSuccessResult[]每个元素包含request本次请求的原始值、type: success与responseTData[]errored: ImportErrorResult[]每个元素包含request、type: error与responseHttpError[]。注意使用onFinish时不要遗漏await因为handleChange返回的是一个Promise。在测试中onFinish也被用来断言传入数据提供者的create/createMany参数见 packages/core/src/hooks/import/index.spec.tsx。meta如果你想向create/createMany方法发送额外的元数据可以使用metauseImport({ meta: { foo: bar, }, });从 core 实现看meta会先经过useMeta()与资源信息合并成combinedMeta再作为meta参数传给create/createMany的 mutation见 packages/core/src/hooks/import/index.tsx。这在需要向 API 传递select、custom等数据提供者特定参数时很有用。onProgress导入进度变化时触发的回调参数为{ totalAmount, processedAmount }分别表示总行数与已处理行数useImport({ onProgress: ({ totalAmount, processedAmount }) { // progress percentage console.log((processedAmount / totalAmount) * 100); }, });core 版本通过useEffect在totalAmount/processedAmount状态变化时触发该回调见 packages/core/src/hooks/import/index.tsx。antd 版本默认会展示一个包含进度百分比的通知环形Progress Importing: x/y 文案完成 4.5 秒后自动关闭你可以通过传入自定义onProgress覆盖这一默认行为见 packages/antd/src/hooks/import/index.tsx。在 packages/core/src/hooks/import/index.spec.tsx 中onProgress被断言会收到{ totalAmount: 3, processedAmount: 3 }这样的最终值。dataProviderName当你配置了多个dataProvider时用dataProviderName指定本次导入使用哪一个useImport({ dataProviderName: second-data-provider, });它会被原样传给 mutation 的dataProviderName参数见 packages/core/src/hooks/import/index.tsx适用于不同资源由不同数据提供者服务的场景。Return Values 返回值useImportantd 版本返回uploadProps、buttonProps、isLoading与mutation。类型上它去掉了 core 版本的handleChange/inputProps替换为面向 Ant Design 的uploadProps/buttonProps见 packages/antd/src/hooks/import/index.tsx。buttonProps与 Ant DesignButton组件兼容的按钮属性import { useImport } from refinedev/antd; import { Button } from antd; export const PostList: React.FC () { const { buttonProps } useImport(); return Button {...buttonProps}Import/Button; };内部细节type默认为defaultloading导入进行中时为true用于切换按钮的加载态。见 packages/antd/src/hooks/import/index.tsx。uploadProps与 Ant DesignUpload组件兼容的上传属性import { useImport } from refinedev/antd; import { Upload } from antd; export const PostList: React.FC () { const { uploadProps } useImport(); return Upload {...uploadProps}Import/Upload; };内部细节见 packages/antd/src/hooks/import/index.tsx属性默认值说明onChangehandleChange处理文件上传触发 CSV 解析与导入流程beforeUpload() false阻止文件被自动上传导入逻辑完全由本 Hook 接管showUploadListfalse隐藏上传文件列表accept.csv只允许选择 CSV 文件isLoading布尔值表示导入是否正在进行见 packages/antd/src/hooks/import/index.tsx。core 实现中它由handleChange开始时置true、handleFinish与handleCleanup时复位见 packages/core/src/hooks/import/index.tsx。测试中也会通过waitFor等待isLoading回到false来确认流程结束见 packages/core/src/hooks/import/index.spec.tsx。mutationuseCreate或useCreateMany的结果取决于batchSize。core 实现中会根据batchSize 1选择useCreate否则选择useCreateMany见 packages/core/src/hooks/import/index.tsx。类型上它是两种UseMutationResult的联合见 packages/core/src/hooks/import/index.tsxUseMutationResult{ data: TData }, TError, { resource: string; values: TVariables; }, unknown | UseMutationResult{ data: TData[] }, TError, { resource: string; values: TVariables[]; }, unknown关于useCreate/useCreateMany的细节可参考 documentation/docs/data/hooks/use-create/index.md 与 documentation/docs/data/hooks/use-create-many/index.md。FAQ关系数据的处理导入时经常遇到的一个场景是CSV 中存放的是外键 ID而你的后端 API 要求的是嵌套对象结构。此时可以用mapData完成扁平 CSV → 嵌套 API 结构的还原。假设你的 CSV 文件内容如下title,content,status,categoryId,userId dummy title 1,dummy content 1,rejected,3,8 dummy title 2,dummy content 2,draft,44,8 dummy title 3,cummy content 3,published,41,10由于user和category是关系字段导出时只保存了它们的 IDuserId、categoryId。要基于该文件重建资源需要把数据映射回后端 API 要求的格式useImportIPostFile({ mapData: (item) { return { title: item.title, content: item.content, status: item.status, category: { id: item.categoryId, }, user: { id: item.userId, }, }; }, }); interface IPostFile { title: string; status: string; content: string; categoryId: string; userId: string; }通过mapData解析出的每行数据在发送给数据提供者之前会被转换成{ title, content, status, category: { id }, user: { id } }结构从而满足后端 API 的约束。泛型参数TItem此处为IPostFile让这一变换过程具备类型安全。从底层看CSV 解析结果先经过importCSVMapper处理第一行作为表头其余每行与表头通过zipfromPairs组合成对象再依次交给mapData变换见 packages/core/src/definitions/helpers/importCSVMapper/index.ts。所以mapData收到的item已经是以表头为 key 的对象。API Reference 速览完整属性表属性类型默认值说明resourcestring从路由读取的资源名指定调用create/createMany的资源mapDataMapDataFnTItem, TVariables(item) item发送前对每行解析结果做变换paparseOptionspapaparse.ParseConfig-传给 Papa Parse 的解析选项batchSizenumberNumber.MAX_SAFE_INTEGER为 1 时逐行调用create大于 1 时按批调用createManyonFinish(results) void-全部请求结束后回调含succeeded/erroredmetaMetaQuery-传给数据提供者方法的额外元数据onProgress(params) voidantd 默认进度通知进度回调参数为{ totalAmount, processedAmount }dataProviderNamestring-多数据提供者时指定使用哪一个返回值表属性说明类型buttonProps与 Ant DesignButton兼容的属性ButtonPropsuploadProps与 Ant DesignUpload兼容的属性UploadPropsisLoading导入进行中的 loading 状态booleanmutation创建导入资源的 mutation/mutations 结果UseMutationResult{ data: TData }, ...|UseMutationResult{ data: TData[] }, ...见上文mutation一节类型参数类型参数说明默认值TItem解析后的 CSV 数据接口anyTData数据查询结果类型继承BaseRecordBaseRecordTError自定义错误对象继承HttpErrorHttpErrorTVariablesmutation 函数的入参类型any从源码看完整导入流程把上面所有信息串起来一次导入的完整生命周期如下依据 packages/core/src/hooks/import/index.tsx 的handleChange实现触发antd 的Upload触发onChange即handleChangebeforeUpload返回false阻止自动上传重置状态handleCleanup将totalAmount/processedAmount归零、isLoading置true解析papaparse.parse读取文件complete回调拿到原始二维数组importCSVMapper依据表头将其转换为对象数组并应用mapData见 packages/core/src/definitions/helpers/importCSVMapper/index.ts分批totalAmount记录总行数若batchSize 1则每行一个请求create否则用lodash/chunk切块后每块一个请求createMany并通过sequentialPromises顺序执行每完成一个请求/批次就更新processedAmount从而驱动onProgress收尾所有请求完成后按type拆分为succeeded/errored传给onFinishisLoading复位antd 版本在完成 4.5 秒后关闭默认进度通知。对应行为都有测试覆盖例如 packages/core/src/hooks/import/index.spec.tsx 验证了batchSize: 2时createMany按两行一批被调用、batchSize: 1时create逐行被调用、mapData在请求前生效、onFinish能收到成功/失败结果、onProgress收到最终进度等。antd 侧的ImportButton /测试则直接复用refinedev/ui-tests的buttonImportTests见 packages/antd/src/components/buttons/import/index.spec.tsx保证组件与 Hook 的行为稳定。小结useImport将选择 CSV → 解析 → 变换 → 分批写入数据提供者 → 展示进度与结果这条完整链路封装在一个 Hook 中配置层面有resource、mapData、paparseOptions、batchSize、onFinish、meta、onProgress、dataProviderName八个选项返回层面有buttonProps、uploadProps、isLoading、mutation四个值与 Ant Design 组件开箱即用。理解其batchSize 决定 create / createMany 切换、sequentialPromises 顺序执行、mapData 在 importCSVMapper 内按行变换这三条实现事实能帮助你在处理大文件批导入、多数据提供者、关系数据还原等真实场景时做出正确的配置决策。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表