
RefineuseImportHook 全解析从 CSV 文件到数据落库的完整导入链路【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refineuseImport是 Refine 提供的数据导入 Hook它让开发者只需几行代码就能把 CSV 文件中的内容解析并批量写入数据源。本文以版本化文档中针对 Ant Design 集成的useImport指南为核心结合本仓库中refinedev/core与refinedev/antd的真实源码与测试用例完整讲解其配置属性、返回值、关联数据处理实战以及底层实现原理帮助你掌握在 Refine 管理后台中构建一键导入能力的完整方案。一、Hook 定位从文件到数据源的桥梁useImport允许你从CSV文件中导入数据对于文件中的每一行它会根据你的配置调用 data provider 的create或createMany方法。内部它使用 Papa Parse 解析文件内容并返回与 Ant DesignUpload和Button组件兼容的属性。该 Hook 是从pankod/refine-core当前仓库中为 refinedev/core的useImportHook 扩展而来因此你可以使用 core 版本的全部能力。core 版本文档见 useImportcore而 Ant Design 集成版的完整实现位于 packages/antd/src/hooks/import/index.tsx。二、快速上手两种基础用法1. 与ImportButton组合使用最简单的用法是直接将useImport的返回值展开到ImportButton上import { useImport, ImportButton } from pankod/refine-antd; export const PostList: React.FC () { const importProps useImport(); return ImportButton {...importProps}Import/ImportButton; };关于ImportButton的完整接口可参考 ImportButton 文档。在源码层面ImportButton 内部就是用 Ant Design 的Upload包裹Button并带上了ImportOutlined图标其label文本由useImportButton()提供hideText属性可控制是否隐藏文字。2. 不使用ImportButton自行组合组件如果你需要更多定制可以直接使用buttonProps与uploadProps手动组合import { useImport, Upload, Button } from pankod/refine-antd; export const PostList: React.FC () { const { buttonProps, uploadProps } useImport(); return ( Upload {...uploadProps} Button {...buttonProps}Import/Button /Upload ); };三、配置属性详解resourceName默认值从当前 URL 读取resource值决定将哪个资源传递给 data provider 的create或createMany方法useImport({ resourceName: posts, });需要说明的是文档中以resourceName命名该参数在 packages/core/src/hooks/import/index.tsx 的ImportOptions类型与实现中该配置项的字段名是resource最终通过useResourceParams({ resource })解析出资源名与标识符identifier再传给create/createMany。core 的测试用例 index.spec.tsx 验证了传入resource: tests时createMany会以resource: tests发起请求。mapData在数据发送给 data provider 之前对其进行映射useImport({ mapData: (data) ({ ...data, category: { id: data.categoryId, }, }), });paparseOptions可以向paparseOptions传入任意 Papa Parse 配置项类型为papaparse.ParseConfig例如header、delimiter、skipEmptyLines等useImport({ paparseOptions: { header: true, }, });在 core 实现中这些选项通过...paparseOptions展开传入papaparse.parse(file, { complete, ...paparseOptions })见 packages/core/src/hooks/import/index.tsx。batchSize默认值Number.MAX_SAFE_INTEGER批量发送数据的大小。当batchSize为 1 时对文件中的每一行调用 data provider 的create方法当batchSize大于 1 时对每个批次调用createMany方法useImport({ batchSize: 1, });重要前提当batchSize大于 1 时你的 data provider 必须实现了createMany方法core 源码中的注释也明确说明了这一点见 packages/core/src/hooks/import/index.tsx。此外默认值Number.MAX_SAFE_INTEGER意味着默认情况下所有数据会一次性全部提交即调用一次createMany。onFinish导入全部结束后触发的回调返回包含succeeded与errored两个数组的对象分别存放成功与失败请求的响应useImport({ onFinish: (result) { // 成功请求的响应 result.succeeded.forEach((item) { console.log(item); }); // 失败请求的响应 result.errored.forEach((item) { console.log(item); }); }, });从源码看succeeded与errored的元素类型为ImportSuccessResult/ImportErrorResult其中request保存本次请求发送的原始数据、response保存响应数据成功时为TData[]失败时为HttpError[]类型定义见 packages/core/src/hooks/import/index.tsx。metaData向 data provider 的create或createMany方法发送额外的元数据如关系字段、自定义请求头等useImport({ metaData: { foo: bar, }, });在 core 实现 中meta即文档中的metaData会经useMeta()与资源自身的 meta 合并为combinedMeta随后传递给create/createMany的meta参数。onProgress导入进度变化时的回调返回totalAmount总行数与processedAmount已处理行数useImport({ onProgress: ({ totalAmount, processedAmount }) { // 进度百分比 console.log((processedAmount / totalAmount) * 100); }, });默认行为如果不传onProgressAnt Design 版本默认会弹出一个带圆形进度条的通知notification.open提示Importing: processedAmount/totalAmount进度完成后约 4.5 秒自动销毁。该默认逻辑实现在 packages/antd/src/hooks/import/index.tsx通知的key为${resource}-import。传入自定义onProgress即可覆盖此行为。dataProviderName当存在多个 data provider 时指定使用哪一个。当你为不同资源配置了不同 data provider 时非常有用useImport({ dataProviderName: second-data-provider, });四、返回值详解buttonProps与 Ant DesignButton组件兼容的按钮属性import { useImport, Button } from pankod/refine-antd; export const PostList: React.FC () { const { buttonProps } useImport(); return Button {...buttonProps}Import/Button; };type默认为default。loading导入进行中时按钮会进入 loading 状态。uploadProps与 Ant DesignUpload组件兼容的上传属性import { useImport, Upload } from pankod/refine-antd; export const PostList: React.FC () { const { uploadProps } useImport(); return Upload {...uploadProps}Import/Upload; };onChange处理文件上传事件。beforeUpload默认为() false阻止文件被自动上传CSV 由 hook 内部读取解析而非直接上传到服务器。showUploadList默认为false隐藏上传文件列表。accept默认为.csv仅接受 CSV 文件。这些默认值在 packages/antd/src/hooks/import/index.tsx 中有明确实现antd 的测试用例也专门验证了beforeUpload返回false见 packages/antd/src/hooks/import/index.spec.ts。isLoading布尔值表示导入是否正在进行中。mutationResultuseCreate或useCreateMany方法的结果。具体调用哪个取决于batchSizebatchSize 1时使用useCreate否则使用useCreateMany见 packages/core/src/hooks/import/index.tsx。对应 Hook 文档可参考 useCreateMany。五、实战处理关联数据Relational Data有时候解析出的 CSV 数据需要进一步处理——例如数据中包含关联字段、引用其他数据或后端 API 要求特定的数据格式。此时可以用mapData来自定义处理过程。例如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 是关联字段导出文件中只保存了它们的 id即userId和categoryId。要从该文件创建资源需要把数据映射回后端 API 要求的格式。mapData可以做到这一点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; }执行这段代码后解析出的数据会被映射为符合 API 要求的格式再提交创建。这一模式在本仓库的官方示例 import-export-antd 中有完整落地列表页通过useImportIPostFile配合mapData将扁平的categoryId/userId还原为嵌套的category: { id }/user: { id }结构再配合ImportButton与ExportButtonuseExport形成导入导出的闭环。示例中IPostFile与IPost的接口定义可参考 examples/import-export-antd/src/interfaces/index.d.ts。六、底层实现原理数据是如何一步步写入数据源的1. 解析Papa Parse importCSVMapper当用户选择 CSV 文件后antd 版本的uploadProps.onChange会调用 core 返回的handleChange见 packages/antd/src/hooks/import/index.tsx。在 core 内部handleChange首先调用papaparse.parse(file, { complete, ...paparseOptions })随后通过importCSVMapper(data, mapData)把解析出的二维数组转换为对象数组export const importCSVMapper TItem any, TVariables any( data: any[][], mapData: MapDataFnTItem, TVariables (item) item as any, ): TVariables[] { const [headers, ...body] data; return body .map((entry) fromPairs(zip(headers, entry))) .map((item: any, index, array: any) mapData.call(undefined, item, index, array), ); };实现位于 packages/core/src/definitions/helpers/importCSVMapper/index.ts第一行作为表头headers用zip将表头与每一行配对、再用fromPairs转为对象最后对每个对象调用mapData回调还能拿到index与完整数组。2. 分批与提交batchSize决定走create还是createMany解析完成后core 会setTotalAmount(values.length)然后按batchSize分支处理batchSize 1为每一行构造一个create.mutateAsync({ resource, values, successNotification: false, errorNotification: false, dataProviderName, meta: combinedMeta })任务batchSize 1用 lodash 的chunk(values, batchSize)把数据切块为每个块构造一个createMany.mutateAsync(...)任务。注意这里显式关闭了successNotification与errorNotification防止每个批次都弹出成功/失败通知并顺序执行所有任务每完成一个任务就把processedAmount增加对应数量单行模式 1批量模式 当前批次长度。3. 顺序执行sequentialPromises无论单行还是批量请求都是**顺序串行**发起的而不是并发。这依赖 sequentialPromisesfor (const [index, promise] of promises.entries()) { try { const result await promise(); results.push(onEachResolve(result, index)); } catch (error) { results.push(onEachReject(error as TReject, index)); } }它逐个await每个任务成功与失败都会被收集为统一的结果数组从而实现对大规模数据导入的节奏控制也能精确统计成功与失败。4. 收尾进度通知与onFinishprocessedAmount/totalAmount的变化会通过useEffect触发onProgress回调所有任务执行完毕后handleFinish将结果按type success/error过滤为{ succeeded, errored }调用onFinish并复位isLoading见 packages/core/src/hooks/import/index.tsx。5. 测试用例佐证core 的测试packages/core/src/hooks/import/index.spec.tsx覆盖了关键行为batchSize: 1时create被按行调用 3 次且参数与解析出的每行数据一致batchSize: 2时createMany按两行一批被调用variables为对应分块batchSize未设置undefined时走createMany且onFinish中succeeded[0].request等于全部解析数据data provider 返回 rejected 时onFinish的errored[0].response[0]能拿到HttpError如statusCode: 500mapData会在提交前完成字段映射onProgress最终以{ totalAmount: 3, processedAmount: 3 }收尾。antd 的测试packages/antd/src/hooks/import/index.spec.ts则验证了uploadProps.beforeUpload返回false以及导入过程中会打开进度通知并在结束后关闭。七、API ReferenceProperties属性类型/默认值说明resourceNamestring默认读当前 URL传递给create/createMany的资源名mapDataMapDataFnTItem, TVariables每条解析记录的映射函数paparseOptionspapaparse.ParseConfig透传给 Papa Parse 的解析配置batchSizenumber默认Number.MAX_SAFE_INTEGER1 时逐行create1 时按批createManyonFinish(results) void全部请求结束后回调返回{ succeeded, errored }metaDataMetaQuery附加元数据随请求传给 data provideronProgress({ totalAmount, processedAmount }) void进度回调默认弹进度通知dataProviderNamestring指定使用哪个 data provider多 provider 场景Return Values属性说明类型buttonProps与 Ant DesignButton组件兼容的属性ButtonPropsuploadProps与 Ant DesignUpload组件兼容的属性UploadPropsisLoading可用于处理 Import 操作的 loading 状态booleanmutationResult创建导入资源的 mutation/mutations 的结果UseMutationResult{ data: TData }, TError, { resource: string; values: TVariables }, unknown|UseMutationResult{ data: TData[] }, TError, { resource: string; values: TVariables[] }, unknownType Parameters属性说明默认值TItem解析后的 CSV 数据接口anyTData继承BaseRecord的查询结果类型BaseRecordTError继承HttpError的自定义错误对象HttpErrorTVariablesmutation 函数的参数类型any相关的核心类型与概念可进一步参考 data provider 文档以及 core 版的 useImport 文档 中关于BaseRecord、HttpError的说明。结语通过useImportRefine 将CSV 解析 → 字段映射 → 分批写入 → 进度反馈 → 结果汇总这一完整链路封装成了一个声明式 Hook只需把它接入 Ant Design 的Upload/Button或现成的ImportButton再配合mapData处理关联数据、batchSize控制写入粒度、onFinish汇总成败结果即可在管理后台快速交付稳定可靠的批量数据导入功能。深入理解其底层对 Papa Parse、importCSVMapper与sequentialPromises的运用也能帮助你在面对大文件、复杂关系映射或自定义数据源时做出更合理的取舍与扩展。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考