Published on

使用 material-table 的踩坑经历

Authors
  • avatar
    Name
    Adam Liu
    Twitter

一种高效使用第三方 React UI 控件的通用思路

直接给你答案: useRef

高效使用意味着:对于一个卡了你几个小时的 bug 或者功能实现,本文的方法可能会帮助你直接秒杀,然后愉快地早点下班做爱做的事。

Intro

写 React 的前端程序员,应该没有谁不使用第三方库的。这些控件可能是 UI 控件、状态管理和测试库等等,你可以在这里了解更多类型的第三方库:awesome-react

我在公司的前端项目频繁使用了 material-table ,因为项目是一个控制面板 Dashboard,所以使用 table 基本是不可避免的。这个第三方 UI 库是 materia-ui 官方文档推荐的。

这个 UI 库功能很强大(支持 pagination,grouping,sorting,style customisation),基本上可以无脑直接采用。对于项目初期快速做功能原型,其实是没有什么问题的。

然鹅

如果你看 material-table 的 demo,分页、PageSize 和排序等会有这样的问题:

  • 你用鼠标点去了第 3 页,一刷新页面,你又回到了第 1 页。PageSize 和排序也是有这个问题,一刷新就回到默认的位置。

上面的问题用户体验是很差的,比如:

  • 一刷新所有状态重置。
  • 不能记住用户的选择状态(比如,排序和 PageSize)。
  • 用户不能分享链接,直接跳到某一页。
  • History Go Back 或者 Go Forward 回不到历史状态。

如何记住用户选择的页面状态?

把用户的选择放在 URL 上面。(当然要看什么页面,一些表单提交页面不适用了)

比如下面的样子:

example.com/#/alerts?deviceModel=Well1&orderBy=created_at&pageSize=10&status=active,silenced

表格的状态更改都会在 URL 里面呈现。比如用户点了下一页,换了排序条件或者改变了内容筛选的条件等等。

那问题来了,可以把 material-table 改为想要的样子吗?

什么样子呢?就是:

  • 刷新页面后,控件能获取 url 的 QueryString 的页码排序等数据,然后再 fetch api 的数据,渲染 material-table
  • History back and forward 也可以实现上一步效果。

这个问题还有第二个问法:我可以直接在父控件只使用 props 把子控件改为想要的样子吗?

答案是:No or Yes.

No. 如果你只是看material-table props option文档。你会发现,按照 react 的标数据流做法(改变 props),是不能传 data 同时改页码,pageSize 和 sorting 的。

等等,props option 里面不是有 initialPage 和 pageSize 吗,控件每次渲染的时候再改变这两个值不就可以了?initialPage 可以,但是,pageSize 和 sorting 并没有给你修改的参数。

Yes.

YES NO.1 把 table 的各个部分替换为自己的控件

material-table 支持把各个部分替换为自己的控件。比如 pagination 选择器,pageSize 和 sorting header 等。你可以直接用material-ui 的 table 部件替换。如果你把各个部件替换了,其实你就是等同于做了一个自己的 table 控件。那就没必要使用 material-table 了。

如果你把 table 的各个部分都替换了,你还要自己管理状态。这点会很麻烦,毕竟代码是写多错多。我同事一开始就是使用了 material-ui 的table-pagination替换了 material-table 的 pagination 选择器。代码量直接翻倍,毕竟我们都是想用第三方控件偷懒。

YES No.2 使用 useRef

你还记得getElementById吗?

在没有 react 之前,前端程序员基本都是要直接操作 DOM 的。熟悉使用各种 DOM 选择器是上古时代前端程序员的基本技能。

有了 react 之后,有了 Virtual DOM,还有这种方便的控件。一层套一层,你可能已经忘记了上古年代直接getElementById然后_innerHTML_的历史。

慢着!对于 material-table,我们能直接拿到这个 DOM 吗?

可以的。当然不是真是 DOM,我们拿的是 material-table 整个实例对象。

对于 React,useRef 可以直接拿到 DOM,也能拿到 component 实例。你可以在这里了解更多:

拿到 ref 之后有啥用?

直接想怎么改就怎么改。material-table 内部是通过 DataManager 管理整个表格的状态和行为的,你可以去这里的源码了解具体的接口。各种内部的方法因有尽有,都是文档都没有提及的。

你可以去下面的 issue 看看具体的案例分析。这个答案就是我提供的。

https://github.com/mbrn/material-table/issues/1480#issuecomment-604289636

具体的流程是:

null

一图胜千言

总结

  • react 是单向数据流的,除了使用 props 改变子控件状态,还可以试一下 useRef。
  • 获取控件的 ref 后,可以看源码获取更深入的修改方法。

完。

本文写作耗时约 2 小时。