尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Refine useDataGrid Hook 完全指南:为 MUI X DataGrid 集成分页、排序、筛选与行内编辑

Refine useDataGrid Hook 完全指南:为 MUI X DataGrid 集成分页、排序、筛选与行内编辑 Refine useDataGrid Hook 完全指南为 MUI X DataGrid 集成分页、排序、筛选与行内编辑【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine导读useDataGrid是 Refine v5 中面向 Material UI 生态的核心 Hook它把 Refine 的useList数据获取能力与 MUI XDataGrid组件无缝桥接让你无需手写状态同步即可获得开箱即用的服务端分页、排序、筛选与行内编辑能力。读完本文你将掌握useDataGrid的全部配置项、返回值与边界场景处理方式能够直接在 Refine 项目中搭建功能完整的 Material UI 数据表格页面。概览useDataGrid 是什么通过useDataGrid你可以直接拿到与 MUI XDataGrid组件兼容的 props排序sorting、筛选filtering和分页pagination等核心功能全部开箱即用。底层数据获取基于 Refine 的useListHook因此数据请求、缓存与失效逻辑都复用了 Refine 的数据层能力。以下几点是使用前必须了解的特性兼容 MUI X 社区版的DataGrid与商业版DataGridPro该 Hook 从refinedev/core的useTable扩展而来因此useTable的全部特性在useDataGrid中同样可用默认从当前路由推断resource无需显式指定资源名。从源码实现看useDataGrid的核心逻辑位于 packages/mui/src/hooks/useDataGrid/index.ts它内部调用useTableCore来自refinedev/core的useTable并将 Refine 的CrudSorting/CrudFilters状态与 MUI X 的GridSortModel/GridFilterModel相互转换最终拼装成dataGridProps返回。转换逻辑集中在 packages/mui/src/definitions/dataGrid/index.ts下文会深入讲解。基础用法在最基本的用法中useDataGrid会原样返回接口的数据默认从 URL 读取resourceimport { List, useDataGrid } from refinedev/mui; import { DataGrid, type GridColDef } from mui/x-data-grid; const columns: GridColDef[] [ { field: id, headerName: ID, type: number }, { field: title, headerName: Title }, { field: status, headerName: Status }, ]; export const PostsList: React.FC () { const { dataGridProps } useDataGridIPost(); return ( List DataGrid {...dataGridProps} columns{columns} / /List ); };只需把dataGridProps展开到DataGrid上分页、排序、筛选即可直接工作。仓库中的完整可运行示例位于 examples/table-material-ui-use-data-grid/src/pages/posts/list.tsx其中演示了editable、syncWithLocation、初始筛选与初始排序的组合使用。分页PaginationuseDataGrid通过设置与DataGrid兼容的paginationMode、paginationModel和onPaginationModelChange三个 props 来处理分页。启用syncWithLocation后分页状态还会同步到 URL 查询参数中。如果你希望在客户端完成分页可以给useDataGrid传入pagination.mode并设为client。默认情况下dataGridProps已经包含分页所需的三件套你可以像下面这样将它们显式拆分后传给DataGridexport const PostsList: React.FC () { const { dataGridProps } useDataGrid(); const { // highlight-start paginationMode, paginationModel, onPaginationModelChange, // highlight-end ...restDataGridProps } dataGridProps; return ( List DataGrid columns{columns} {...restDataGridProps} // highlight-start paginationMode{paginationMode} paginationModel{paginationModel} onPaginationModelChange{onPaginationModelChange} // highlight-end / /List ); };从源码可以看到 packages/mui/src/hooks/useDataGrid/index.ts 中dataGridPaginationValues的实现MUI X 的page从0开始而 Refine 的currentPage从1开始因此 Hook 在拼接paginationModel时做了page: currentPage - 1的换算当分页模式为off时则返回paginationMode: client并省略分页模型。对应的测试用例在 packages/mui/src/hooks/useDataGrid/index.spec.ts 中验证了client/server模式下的 props 输出以及off模式下不设置paginationModel的行为。排序Sorting排序由 Hook 自动处理它会设置sortingMode、sortModel和onSortModelChange三个与DataGrid兼容的 props并同样支持通过syncWithLocation与 URL 同步。export const PostsList: React.FC () { const { dataGridProps } useDataGrid(); // highlight-start const { sortingMode, sortModel, onSortModelChange, ...restDataGridProps } dataGridProps; // highlight-end return ( List DataGrid columns{columns} {...restDataGridProps} // highlight-start sortingMode{sortingMode} sortModel{sortModel} onSortModelChange{onSortModelChange} // highlight-end / /List ); };在 DataGrid 外部控制排序useDataGrid返回的setSorters函数可以接收CrudSorting类型的排序数组因此你可以在DataGrid之外例如工具栏按钮触发排序import { useDataGrid, List } from refinedev/mui; import { Button, ButtonGroup } from mui/material; import { DataGrid, GridColDef } from mui/x-data-grid; const columns: GridColDef[] [ { field: id, headerName: ID, type: number }, { field: title, headerName: Title }, { field: status, headerName: Status }, ]; export const PostsList: React.FC () { const { dataGridProps, setSorters } useDataGrid(); const handleSorting (order: asc | desc) { setSorters([{ field: title, order }]); }; return ( List ButtonGroup variantoutlined Button onClick{() handleSorting(asc)}Asc/Button Button onClick{() handleSorting(desc)}Desc/Button /ButtonGroup DataGrid {...dataGridProps} columns{columns} / /List ); };多列排序的两种路径MUI X 社区版一次只按一个条件对行排序若要使用界面上的多列排序需要升级到 Pro 计划。但多列排序可以在服务端完成——只要不显式传入sortModelreturn DataGrid {...dataGridProps} sortModel{undefined} /;不传sortModel时服务端支持同时按多个条件排序代价是DataGrid的表头无法显示当前哪些字段处于排序状态。从源码看handleSortModelChangepackages/mui/src/hooks/useDataGrid/index.ts调用transformSortModelToCrudSorting把 MUI 的GridSortModel转成 Refine 的CrudSorting再交给setSorters反向的transformCrudSortingToSortModel用于把 Refine 排序状态渲染回 MUI 的sortModel。两个转换函数都位于 packages/mui/src/definitions/dataGrid/index.ts格式即{ field, sort }与{ field, order }之间的映射。筛选Filtering筛选同样由 Hook 自动处理设置filterMode、filterModel和onFilterModelChange三个 props并支持通过syncWithLocation与 URL 同步。export const PostsList: React.FC () { const { dataGridProps } useDataGrid(); // highlight-start const { filterMode, filterModel, onFilterModelChange, ...restDataGridProps } dataGridProps; // highlight-end return ( List DataGrid columns{columns} {...restDataGridProps} // highlight-start filterMode{filterMode} filterModel{filterModel} onFilterModelChange{onFilterModelChange} // highlight-end / /List ); };在 DataGrid 外部控制筛选类似排序你可以用返回的setFilters在组件外部设置筛选条件例如用一个复选框控制 draft 状态import { useDataGrid, List } from refinedev/mui; import { FormControlLabel, Checkbox } from mui/material; import { DataGrid, GridColDef } from mui/x-data-grid; const columns: GridColDef[] [ { field: id, headerName: ID, type: number }, { field: title, headerName: Title }, { field: status, headerName: Status }, ]; export const PostsList: React.FC () { const { dataGridProps, setFilters } useDataGrid(); const handleFilter ( e: React.ChangeEventHTMLInputElement, checked: boolean, ) { setFilters([ { field: status, value: checked ? draft : undefined, operator: eq, }, ]); }; return ( List FormControlLabel labelFilter by Draft Status control{Checkbox onChange{handleFilter} /} / DataGrid {...dataGridProps} columns{columns} / /List ); };多条件筛选的两种路径与排序同理MUI X 社区版界面一次只支持一个筛选条件多条件界面筛选需要 Pro 计划但服务端多条件筛选无需指定filterModel即可工作return DataGrid {...dataGridProps} filterModel{undefined} /;不传filterModel时支持同时筛选多个字段但无法在DataGrid表头展示当前生效的筛选条件。读取当前筛选值getDefaultFilterRefine 提供了getDefaultFilter函数实现在packages/core/src/definitions/table/index.ts中可以用来读取某个字段当前的筛选值import { getDefaultFilter } from refinedev/core; import { useDataGrid } from refinedev/mui; const MyComponent () { const { filters } useDataGrid({ filters: { initial: [ { field: name, operator: contains, value: John Doe, }, ], }, }); const nameFilterValue getDefaultFilter(name, filters, contains); console.log(nameFilterValue); // John Doe return { /** ... */ }; };服务端筛选的防抖处理值得注意的一个实现细节源码中定义了DEFAULT_FILTER_DEBOUNCE_MS 300packages/mui/src/hooks/useDataGrid/index.ts。服务端筛选模式下handleFilterModelChange会先更新本地筛选状态让输入即时响应再通过 300ms 的防抖延迟发起服务端请求同文件第 262-274 行避免每次击键都触发网络请求同时把filterDebounceMs设为0以禁用 MUI X 自带的防抖防止输入被重置。MUI 运算符与 Refine 运算符的映射在 packages/mui/src/definitions/dataGrid/index.ts 中Refine 实现了 MUI 运算符equals、contains、isAnyOf、after、before等与 RefineCrudOperatorseq、contains、in、gt、lt等之间的双向转换并针对列的typenumber、singleSelect、string、date/dateTime等选择最合适的运算符表达。这意味着你在DataGrid界面上选择的筛选条件会被精确翻译成 data provider 能理解的CrudFilters结构。实时更新Realtime UpdatesuseDataGrid支持 Refine 的实时Live能力但需要配置LiveProvider才能生效。Hook 挂载时会以channel、resource等参数调用liveProvider的subscribe方法订阅实时事件适合需要展示实时变化数据的场景。与实时相关的配置项包括liveMode收到相关实时事件后决定自动更新数据auto还是手动处理manualonLiveEvent订阅到新事件时执行的回调liveParams传给liveProvider.subscribe方法的额外参数。行内编辑EditinguseDataGrid扩展了 MUIDataGrid的编辑能力。要开启列编辑在列定义上设置editable: trueconst columns React.useMemoGridColDefIPost[]( () [ { field: title, headerName: Title, minWidth: 400, flex: 1, editable: true, }, ], [], );编辑背后的 useUpdate 集成Refine v5 中useDataGrid借助useUpdate直接与更新操作集成省去了手动管理表单状态转换的复杂度性能更好、交互模型更简洁。Hook 通过formProps暴露processRowUpdate与formLoadingconst { dataGridProps, formProps: { processRowUpdate, formLoading }, } useDataGridIPost();默认情况下单元格编辑开始并完成后processRowUpdate会被触发内部调用useUpdate的mutate函数提交变更。核心流程如下源码位于 packages/mui/src/hooks/useDataGrid/index.tsconst processRowUpdate async (newRow: TData, oldRow: TData) { try { await new Promise((resolve, reject) { mutate( { resource: resourceFromProp as string, id: newRow.id as string, values: newRow, }, { onError: (error) { reject(error); }, onSuccess: (data) { resolve(data); }, }, ); }); return newRow; } catch (error) { return oldRow; } };几个值得留意的细节只有editable: true时processRowUpdate才会真正执行更新否则直接resolve(oldRow)如果identifier无法解析例如没有配置 resource会 reject 一个Resource is not defined错误updateMutationOptions可以透传meta等选项给mutate测试用例 packages/mui/src/hooks/useDataGrid/index.spec.ts 验证了meta会被正确传递到 data provider 的update方法。配置项详解Properties下面逐一说明useDataGrid的核心配置项、默认值与使用场景。完整类型定义可见 packages/mui/src/hooks/useDataGrid/index.ts 中的UseDataGridProps。resourceresource默认从当前路由推断存在多个同名资源时可以用identifier作为主匹配键data provider 方法仍使用Refine/组件中定义的资源name。useDataGrid({ resource: categories, });dataProviderName当项目配置了多个dataProvider时用dataProviderName指定某个资源使用哪一个useDataGrid({ dataProviderName: second-data-provider, });pagination.currentPage设置初始页码默认值为1useDataGrid({ pagination: { currentPage: 2, }, });pagination.pageSize设置初始每页条数默认值为25useDataGrid({ pagination: { pageSize: 10, }, });pagination.mode取值off、server或client默认serveroff禁用分页获取全部记录client客户端分页先获取全部记录再在客户端分页server服务端分页按currentPage和pageSize请求数据。useDataGrid({ pagination: { mode: client, }, });sorters.initial设置排序的初始值。initial不是永久的用户改变排序后会被清除如需永久生效使用sorters.permanentuseDataGrid({ sorters: { initial: [{ field: name, order: asc }], }, });sorters.permanent设置永久的、不可变更的排序值。用户改变排序时不会被清除如需临时值使用sorters.initialuseDataGrid({ sorters: { permanent: [{ field: name, order: asc }], }, });sorters.mode取值off或server默认serveroff排序值不发送到服务端可在客户端自行排序server服务端排序按sorters值请求数据。useDataGrid({ sorters: { mode: server, }, });filters.initial设置筛选的初始值。同样不是永久的用户修改筛选后会被清除如需永久生效使用filters.permanentuseDataGrid({ filters: { initial: [{ field: name, operator: contains, value: Foo }], }, });filters.permanent设置永久的、不可变更的筛选值useDataGrid({ filters: { permanent: [{ field: name, operator: contains, value: Foo }], }, });filters.defaultBehavior筛选行为可以是merge或replace默认mergemerge新筛选与已有筛选合并——同字段的新筛选替换旧筛选不同字段的新筛选追加到已有筛选replace用新筛选整体替换已有筛选。该默认值也可以通过setFilters的第二个参数按次覆盖。注意一个实现细节useDataGrid在把filters透传给底层useTableCore时会强制将defaultBehavior固定为replace见 packages/mui/src/hooks/useDataGrid/index.ts以保证与 MUI DataGrid 的filterModel行为一致而setFilters的第二个参数仍可传入merge或replace按次覆盖。useDataGrid({ filters: { defaultBehavior: replace, }, });filters.mode取值off或server默认serveroff筛选值不发送到服务端可在客户端自行筛选server服务端筛选按filters值请求数据。useDataGrid({ filters: { mode: off, }, });syncWithLocation 与 URL 状态同步启用syncWithLocation后useDataGrid的状态排序、筛选、分页会自动编码进 URL 查询参数当 URL 变化时Hook 状态也会自动跟随更新。这使得表格状态可以在不同路由/页面之间共享用户还可以通过书签或分享链接直达某个特定表格视图。默认值为falseuseDataGrid({ syncWithLocation: true, });也可以在Refine/组件上全局开启该功能。queryOptionsuseDataGrid底层通过useList获取数据因此可以直接传入 TanStack Query 的queryOptionsuseDataGrid({ queryOptions: { retry: 3, }, });metameta用于向 data provider 方法传递额外信息典型用途包括针对特定用例定制 data provider 方法、用纯 JS 对象生成 GraphQL 查询。例如向getList传递自定义请求头useDataGrid({ meta: { headers: { x-meta-data: true }, }, }); const myDataProvider { //... getList: async ({ resource, pagination, sorters, filters, // highlight-next-line meta, }) { // highlight-next-line const headers meta?.headers ?? {}; const url ${apiUrl}/${resource}; //... //... // highlight-next-line const { data, headers } await httpClient.get(${url}, { headers }); return { data, }; }, //... };successNotification 与 errorNotification两者都需要NotificationProvider支持。数据获取成功或失败时Hook 会调用open函数展示通知你可以通过这两个 prop 自定义通知内容useDataGrid({ successNotification: (data, values, resource) { return { message: ${data.title} Successfully fetched., description: Success with no errors, type: success, }; }, }); useDataGrid({ errorNotification: (data, values, resource) { return { message: Something went wrong when getting ${data.id}, description: Error, type: error, }; }, });overtimeOptions当请求耗时过长时可用overtimeOptions展示加载提示。interval是毫秒级的时间间隔onInterval是每个间隔触发的回调Hook 返回的overtime.elapsedTime是已耗时的毫秒数请求完成后变为undefinedconst { overtime } useDataGrid({ //... overtimeOptions: { interval: 1000, onInterval(elapsedInterval) { console.log(elapsedInterval); }, }, }); console.log(overtime.elapsedTime); // undefined, 1000, 2000, 3000 4000, ... // 使用示例 { elapsedTime 4000 divthis takes a bit longer than expected/div; }返回值详解Return ValuesdataGridPropsDataGrid组件所需的 props包含以下字段sortingMode是否服务端排序默认serversortModel当前与DataGrid兼容的GridSortModelonSortModelChange用户排序某列时以新排序模型调用。该函数会自动把GridSortModel转换为CrudSorting并调用setSorters。需要覆盖时可以这样包装DataGrid {...dataGridProps} columns{columns} onSortModelChange{(model, details) { dataGridProps.onSortModelChange(model, details); // do something else }} /filterMode是否服务端筛选默认serverfilterModel当前与DataGrid兼容的GridFilterModelonFilterModelChange用户筛选某列时以新筛选模型调用。该函数会自动把GridFilterModel转换为CrudFilters并调用setFilters。覆盖方式同理DataGrid {...dataGridProps} columns{columns} onFilterModelChange{(model) { dataGridProps.onFilterModelChange(model); // do something else }} /onStateChange用户排序或筛选某列时以新状态调用useDataGrid内部用它跟踪列类型columnsTypes以便把 MUI 运算符按列类型正确转换回 Refine 运算符。覆盖方式DataGrid {...dataGridProps} columns{columns} onStateChange{(state) { dataGridProps.onStateChange(state); // do something else }} /rows表格展示的数据由useList获取rowCount数据总数由useList获取loading是否正在获取数据pagination分页配置值pageSize、currentPage、setCurrentPage等。tableQueryuseList的完整返回结果即 TanStack Query 的useQuery结果。sorters / setSorterssorters当前排序状态CrudSortingsetSorters设置排序状态的函数签名(sorters: CrudSorting) void。filters / setFiltersfilters当前筛选状态CrudFilterssetFilters设置筛选状态的函数签名((filters: CrudFilters, behavior?: SetFilterBehavior) void) ((setter: (prevFilters: CrudFilters) CrudFilters) void);分页相关状态currentPage当前页码分页禁用时为undefinedsetCurrentPageReact.DispatchReact.SetStateActionnumber | undefinedpageSize当前每页条数分页禁用时为undefinedsetPageSize同上类型pageCount总页数分页禁用时为undefined。createLinkForSyncWithLocation签名(params: SyncWithLocationParams) string用于为syncWithLocation生成可访问的链接。overtime{ elapsedTime?: number }已耗时毫秒数请求完成时变为undefinedconst { overtime } useDataGrid(); console.log(overtime.elapsedTime); // undefined, 1000, 2000, 3000 4000, ...searchsearch会把接收到的参数发送给onSearch函数(value: TSearchVariables) Promisevoid。你传入的onSearch返回CrudFilters随后这些筛选会被应用并重置到第一页。仓库测试用例 packages/mui/src/hooks/useDataGrid/index.spec.ts 演示了通过onSearchsearch实现受控搜索的完整流程。常见问题FAQ如何处理关联数据relational data可以使用useSelect获取关联数据再结合valueOptions与renderCell在DataGrid中展示。参考 examples/table-material-ui-use-data-grid/src/pages/posts/list.tsx 中的category.id列它通过useSelect拉取categories资源把选项传给valueOptions并用renderCell将category.id渲染为分类名称。如何实现客户端筛选设置filters.mode: off即可禁用服务端筛选此时useDataGrid与 MUIDataGrid自身的筛选功能完全兼容useDataGrid({ filters: { mode: off, }, });如何实现客户端排序设置sorters.mode: off即可禁用服务端排序useDataGrid与 MUIDataGrid的排序功能完全兼容useDataGrid({ sorters: { mode: off, }, });类型参数Type Parameters参数说明类型默认值TQueryFnDataquery 函数返回的结果数据类型继承BaseRecordBaseRecordBaseRecordTError继承HttpError的自定义错误类型HttpErrorHttpErrorTSearchVariables搜索参数的类型{}TDataselect函数返回的结果数据类型继承BaseRecord未指定时默认取TQueryFnData的值BaseRecordTQueryFnData典型用法const { dataGridProps } useDataGridIPost, HttpError, IPostSearch();完整示例仓库中 examples/table-material-ui-use-data-grid 提供了开箱即用的完整示例项目App.tsx中通过refinedev/simple-rest指向https://api.fake-rest.refine.dev注册posts资源并挂载 Material UI 主题list.tsx中把editable、syncWithLocation、初始分页/筛选/排序组合起来展示了useDataGrid的典型实战形态。export const PostList: React.FC () { const { dataGridProps } useDataGridIPost({ editable: true, syncWithLocation: true, pagination: { currentPage: 1, pageSize: 10, }, filters: { initial: [ { field: status, operator: eq, value: draft, }, ], }, sorters: { initial: [ { field: title, order: asc, }, ], }, }); return ( List DataGrid {...dataGridProps} columns{columns} pageSizeOptions{[10, 20, 30, 50, 100]} / /List ); };总结useDataGrid是 Refine v5 中打通数据层与界面层的桥梁它把useList的取数能力、useTable的表格状态管理以及 MUI XDataGrid的分页/排序/筛选/编辑交互封装为一套开箱即用的 props。掌握其配置项语义尤其是pagination.mode、sorters.mode、filters.mode与syncWithLocation并理解 packages/mui/src/definitions/dataGrid/index.ts 中的模型转换机制你就能在项目中快速构建专业级的 Material UI 数据表格同时保持 Refine 数据层的灵活性与可扩展性。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表