十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Refine 中的 CSV 文件导入:从 React 原生实现到 useImport 钩子的完整实践

Refine 中的 CSV 文件导入:从 React 原生实现到 useImport 钩子的完整实践 Refine 中的 CSV 文件导入从 React 原生实现到 useImport 钩子的完整实践【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本文以 Refine 官方博客《How to Import CSV File with React》为主线完整讲解如何在 React 中接收用户上传的 CSV 文件并用 JavaScript 解析、展示再进一步结合 Refine 核心的useImport钩子与ImportButton组件把CSV 导入变成管理后台中开箱即用的功能。读完本文你将掌握用input typefileFileReader读取 CSV 原始文本的完整流程、用slice/split将 CSV 文本手工转换为对象数组Array of Object的算法细节以及 Refine 源码层面useImport的分批提交batchSize、进度回调onProgress与结果汇总onFinish的实现原理。一、目标与整体思路CSV 导入是管理面板、内部工具类应用的常见需求用户从本地选择一个 CSV 文件应用读取它、把每一行转换成结构化对象然后渲染到表格或提交给后端。原文档将整个过程拆成两个阶段纯 React JavaScript 阶段创建一个带文件选择框的表单用FileReader读取 CSV 文件内容为纯文本再用字符串操作把它解析成对象数组最后渲染进table元素Refine 集成阶段借助useImport钩子和ImportButton组件把解析 CSV 批量写入后端封装成标准能力支持batchSize分批提交。第一阶段帮助你理解底层原理第二阶段则是 Refine 提供的生产级方案。二、React 原生实现第一步创建文件上传表单第一步是创建一个简单的表单让用户选择 CSV 文件。关键点在input元素的accept属性它指定了只允许选择 CSV 格式的文件function App() { return ( div style{{ textAlign: center }} h1REACTJS CSV IMPORT EXAMPLE /h1 form input type{file} accept{.csv} / buttonIMPORT CSV/button /form /div ); }accept{.csv}只是在文件选择器中过滤扩展名并不是严格的安全校验服务端仍应校验文件内容。三、用 FileReader 读取 CSV 文件内容accept只负责选文件真正读文件要交给浏览器原生的 FileReader API。下面的完整代码在表单中增加了状态管理file状态保存用户选中的File对象handleOnChange在文件变化时更新它handleOnSubmit在提交时通过fileReader.readAsText(file)异步读取文件文本读取完成后fileReader.onload中的event.target.result就是 CSV 的原始字符串import React, { useState } from react; function App() { const [file, setFile] useState(); const fileReader new FileReader(); const handleOnChange (e) { setFile(e.target.files[0]); }; const handleOnSubmit (e) { e.preventDefault(); if (file) { fileReader.onload function (event) { const csvOutput event.target.result; }; fileReader.readAsText(file); } }; return ( div style{{ textAlign: center }} h1REACTJS CSV IMPORT EXAMPLE /h1 form input type{file} id{csvFileInput} accept{.csv} onChange{handleOnChange} / button onClick{(e) { handleOnSubmit(e); }} IMPORT CSV /button /form /div ); }选择并加载 CSV 文件后csvOutput中就是文件的纯文本内容可以在浏览器控制台中直接查看。此时我们拿到的是一整段文本还不能直接渲染或提交下一步要把它结构化为数组。四、把 CSV 文本解析为对象数组并渲染成表格原文档的核心手法是用 JavaScript 的 slice 和 split 方法把纯文本 CSV 拆成表头数组 数据行数组两个独立数组再把 Header 和 Rows 按 Key/Value 对应关系转换成 Array of Object 格式function App() { const [file, setFile] useState(); const [array, setArray] useState([]); const fileReader new FileReader(); const handleOnChange (e) { setFile(e.target.files[0]); }; const csvFileToArray (string) { const csvHeader string.slice(0, string.indexOf(\n)).split(,); const csvRows string.slice(string.indexOf(\n) 1).split(\n); const array csvRows.map((i) { const values i.split(,); const obj csvHeader.reduce((object, header, index) { object[header] values[index]; return object; }, {}); return obj; }); setArray(array); }; const handleOnSubmit (e) { e.preventDefault(); if (file) { fileReader.onload function (event) { const text event.target.result; csvFileToArray(text); }; fileReader.readAsText(file); } }; const headerKeys Object.keys(Object.assign({}, ...array)); return ( div style{{ textAlign: center } h1REACTJS CSV IMPORT EXAMPLE /h1 form input type{file} id{csvFileInput} accept{.csv} onChange{handleOnChange} / button onClick{(e) { handleOnSubmit(e); }} IMPORT CSV /button /form br / table thead tr key{header} {headerKeys.map((key) ( th{key}/th ))} /tr /thead tbody {array.map((item) ( tr key{item.id} {Object.values(item).map((val) ( td{val}/td ))} /tr ))} /tbody /table /div ); }解析逻辑可以逐行拆开看string.slice(0, string.indexOf(\n))取第一行作为表头.split(,)得到列名数组例如[id, title, status]string.slice(string.indexOf(\n) 1)跳过表头行后按换行符split(\n)得到每一行的字符串每行再按逗号split(,)得到values然后用csvHeader.reduce按列索引把values[index]挂到以列名为 key 的对象上最终得到一个对象数组headerKeys通过Object.keys(Object.assign({}, ...array))从数据行对象中聚合出所有列名用于渲染thead。这套手工解析适合简单场景但对字段内包含逗号/换行/引号转义的复杂 CSV 无能为力——这正是 Refine 官方实现改用成熟解析库的原因见后文。原文档中的 CodeSandbox 在线示例可以按同样代码直接复制运行验证效果。五、Refine 集成useImport 与 ImportButton在 Refine 中CSV 导入是开箱即用的功能。最小用法只需引入useImport钩子和ImportButton组件import { List, Table, useTable, useImport, ImportButton, } from refinedev/antd; export const PostList: React.FC () { const { tableProps } useTableIPost(); const importProps useImportIPostFile(); return ( List pageHeaderProps{{ extra: ImportButton {...importProps} /, }} Table {...tableProps} rowKeyid Table.Column dataIndexid titleID / Table.Column dataIndextitle titleTitle / Table.Column dataIndexstatus titleStatus / /Table /List ); }; interface IPostFile { title: string; categoryId: string; } interface IPost { id: string; title: string; status: string; }useImportIPostFile()的泛型参数是 CSV 解析后每条记录的类型importProps展开到ImportButton上后用户点击按钮选文件即可触发导入数据会通过dataProvider写入List当前所在路由对应的资源。官方仓库中还有完整的 import/export 示例工程如 import-export-antd 示例对应博客中链接的import-export示例目录可直接参考。六、源码剖析useImport 是如何工作的上一节的用法来自 useImport 源码它的实现比博客示例聪明得多核心要点如下用 PapaParse 替代手工 slice/splithandleChange内部调用papaparse.parse(file, { complete, ...paparseOptions })解析文件而不是手写字符串切割因此天然支持引号转义、换行等标准 CSV 边界情况。你还可以通过paparseOptions传入自定义的 PapaParseParseConfig表头到对象的映射由 importCSVMapper 完成解析出的二维数组交给 importCSVMapper它用fromPairs(zip(headers, entry))把表头行 数据行逐行压成对象再对每条记录调用mapData回调默认恒等。也就是说博客第四节的csvFileToArray在 Refine 内部由importCSVMapper等价实现但多了 lodash 的稳定处理batchSize 决定走 create 还是 createMany从源码结构看batchSize默认值为Number.MAX_SAFE_INTEGER即所有记录合并为一批、走useCreateMany要求dataProvider实现了createMany当batchSize 1时改为逐条走useCreate。分批通过lodash/chunk切块并用 sequentialPromises 顺序执行避免并发打爆后端。这就是博客可以配合batchSize选项在插入过程中把数据分块的底层出处进度与结果回调每处理完一条/一块setProcessedAmount累加useEffect监听到totalAmount/processedAmount变化后触发onProgress({ totalAmount, processedAmount })全部结束后handleFinish把结果按type success | error拆成succeeded与errored两组交给onFinish便于你统计成功条数或提示失败原因资源与元数据resource未显式传入时从当前路由中读取useResourceParamsmeta会被useMeta()合并后透传给dataProvider存在多个dataProvider时可用dataProviderName指定目标。完整的选项类型定义在ImportOptions中包括resource、mapData、paparseOptions、batchSize、onFinish、onProgress、meta、dataProviderName返回值则包含可直接绑定的inputProps固定type: file、accept: .csv的输入属性、mutationResult、isLoading与handleChange。七、当前文档版本中的用法参考官方文档站中的 useImport 交互式示例展示了与上文一致的完整用法用useImportIProduct({ resource, onFinish, onProgress })拿到inputProps和isLoading把inputProps直接展开到一个input namecsv {...inputProps} /上导入期间界面实时显示processed / total进度完成后通过onFinish弹出提示。需要注意该示例基于较新版本的 API列表数据用useList获取而本博客最初撰写于 Refine 3.x 时代、代码示例已更新到 4.x官方博客注明 4.x 与 3.x 向后兼容迁移差异可参考仓库中的 migration guide 文档。八、小结纯 React 方案input typefile accept.csv选择文件 →FileReader.readAsText读取文本 →slice/split拆表头和数据行 →reduce映射成对象数组 → 渲染table。原理清晰但解析能力有限Refine 方案useImport用 PapaParse 做健壮解析importCSVMapper完成表头到对象的映射batchSize控制useCreateMany批量提交或逐条useCreateonProgress/onFinish提供进度与结果钩子配合ImportButton一行代码即可挂到List页头两个关键源码入口值得深入阅读useImport 实现 与 importCSVMapper以及各 UI 框架下的按钮实现如 antd 的 ImportButton它用 antd 的Upload包裹Button并复用useImportButton的本地化文案。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表