切换日光/暗黑模式
093. 前后置钩子、列配置钩子与选择模块
学习目标
这一节把事件钩子继续扩展到请求前后,并完成索引列、单选、多选模块。
学完后,你应该能理解:
- 查询、新建、更新、删除为什么需要前后置钩子;
- 事件钩子为什么要支持异步和抛错;
- 如何在编辑状态下阻止查询;
- 列配置钩子变量如何让模块追加列;
- 为什么列计算要尽量放到更晚的渲染阶段;
- 索引列、单选、多选模块分别解决什么问题。
请求前后置钩子
前面已经有行点击和行双击事件钩子。
这一节继续增加请求相关钩子。
包括:
- 查询前;
- 查询后;
- 新建前;
- 新建后;
- 更新前;
- 更新后;
- 删除前;
- 删除后。
这些钩子让其他模块可以在请求前后插入逻辑。
例如校验状态、修改请求参数、记录日志、刷新选中数据。
钩子支持异步
请求前后置钩子需要支持异步。
原因是有些动作不是同步完成的。
例如:
- 请求前弹窗确认;
- 请求前检查权限;
- 请求前调用接口生成签名;
- 请求后刷新其他模块数据。
所以执行钩子时使用 await。
这样模块可以返回 Promise。
钩子可以阻断流程
前置钩子可以抛错。
一旦抛错,后面的请求就不继续执行。
例如表格正在编辑时,不允许重新查询。
可以在查询前钩子里判断:
ts
if (autoTable.state.isTableEditing) {
throw new Error("请先保存数据再查询")
}这样用户在编辑状态下翻页或查询时,会被阻止。
这比在每个按钮里写判断更统一。
编辑时不允许查询
如果正在编辑或新建数据,直接翻页会有风险。
未保存数据可能丢失。
所以这一节用 beforeLoad 钩子阻止查询。
当表格处于编辑状态时,翻页和重新加载会抛出提示。
用户保存或取消后,才能继续查询。
这是前端保护数据的一层规则。
新建和更新的前后置
保存时要区分新建和更新。
如果是新建数据,就执行新建前置和新建后置。
如果是已有数据,就执行更新前置和更新后置。
这让模块能分别处理两种场景。
例如:
- 新建后默认选中新行;
- 更新后刷新选中数据;
- 新建前补默认字段;
- 更新前校验版本号。
删除后置
删除后置也很重要。
如果某行被删除,而它正好在多选列表里,就要从选中数据里移除。
否则外部模块拿到的选中数据会包含已删除行。
所以删除完成后,需要通知选择模块同步状态。
这就是后置钩子的价值。
列配置钩子变量
列配置也改造成钩子变量。
它保存当前表格最终要渲染的列。
不同模块都可以往里面添加列。
例如:
- 页面业务列;
- 标准字段列;
- 索引列;
- 多选列;
- 操作列;
- 自动撑开列。
所有列最后统一排序和渲染。
标准字段列
如果项目要求所有表格都显示标准字段,可以通过模块统一添加。
例如:
- ID;
- 创建时间;
- 更新时间。
模块只需要往列配置钩子变量里 push 这些列。
所有使用这个表格的页面都会出现这些字段。
这比每个页面手动复制列配置更稳定。
模块顺序与列计算
列配置来自多个模块。
如果某个模块太早用 useMemo 计算最终列,后面模块再 push 列就来不及了。
这会导致后添加的列不显示。
解决方式是把最终列计算放到更晚的阶段。
课程里把表格内容抽成子组件。
在子组件渲染时再计算列。
这样所有模块都已经完成注册,列配置更完整。
为什么子组件能解决问题
父级模块安装时,各个模块还在往钩子变量里追加列。
如果父级马上计算列,顺序会受模块执行影响。
子组件渲染发生得更晚。
它拿到的是本轮已经收集完成的列配置。
所以标准字段模块、索引列模块、多选列模块即使顺序靠后,也能进入最终列。
索引列模块
索引列被拆成独立列模块。
它负责显示当前行序号。
索引列通常有几个默认值:
- 靠左;
- 固定宽度;
- 居中;
- 标题为序号;
- 顺序值很小,靠近表格最左侧。
索引列显示的不是当前页内索引。
它要结合分页信息计算全局序号。
例如第二页第一条,要显示对应的全局行号。
索引列和编辑态
索引列渲染时要注意编辑态。
非编辑状态和编辑状态传入的 record 可能不是同一个对象。
如果直接用编辑态表单数据去找索引,可能找不到。
所以索引列组件要使用正确的原始行数据来计算序号。
这是通用组件里很容易踩到的细节。
单选模块
单选模块维护当前选中行 ID。
它不直接保存整条行数据。
原因是行数据可能会更新。
如果保存整条对象,更新后还要同步它。
只保存 ID,则可以随时从最新表格数据里找到当前选中行。
这样选中行始终和表格数据保持一致。
自动选中第一行
数据加载完成后,单选模块会默认选中第一行。
原因是后续很多功能需要当前行。
例如:
- 表单编辑;
- 父子表查询;
- 查看详情;
- 外部按钮操作。
如果没有当前行,这些功能就要到处处理空状态。
默认选中第一行能让交互更连续。
单选显示
单选状态可以显示在索引列上。
当某行被选中时,索引列用主题色背景高亮。
点击其他行时,选中 ID 改变。
翻页后,加载完成再选中当前页第一行。
这就形成了基本单选体验。
多选模块
多选模块比单选复杂。
它要支持跨页选择。
如果只保存 ID,翻页后当前页数据可能不存在对应记录。
所以多选模块需要保存选中的行数据。
这带来额外同步问题。
当数据新增、更新、删除、重新加载时,选中数据也要同步。
多选状态同步
多选模块需要监听多个后置事件:
- 加载后刷新选中数据;
- 新建后同步新数据;
- 更新后替换选中数据;
- 删除后移除已删除数据。
如果不处理这些事件,选中数据会变旧。
例如用户选中一行后编辑保存。
外部拿到的选中行应该是更新后的数据,而不是旧数据。
多选列
多选模块会添加多选列。
如果配置开启多选列,就往列钩子变量里添加选择列。
选择列用于显示当前行是否选中。
点击行时,也可以根据配置触发选中或取消选中。
这比只靠 checkbox 更灵活。
全选、半选与反选
多选模块要计算当前页选择状态。
常见状态有:
- 当前页全选;
- 当前页全不选;
- 当前页部分选中。
还要提供方法:
- 全选当前页;
- 取消当前页全选;
- 选中或反选某一行。
这些能力为批量操作做准备。
为什么不用现成表格选择能力
Ant Design Table 自带选择功能。
但课程里的多选要结合:
- 跨页选择;
- 新建后同步;
- 更新后同步;
- 删除后同步;
- 外部模块读取;
- 自定义列和钩子体系。
这些需求超出了简单 rowSelection 的范围。
所以这里自己封装选择模块。
这一节的核心
这一节把表格从“能编辑”继续推向“能被模块联动”。
关键能力包括:
- 请求前后置钩子;
- 编辑状态阻止查询;
- 列配置钩子变量;
- 标准字段模块;
- 索引列模块;
- 单选模块;
- 多选模块;
- 多选数据的跨页与更新同步。
这些能力会继续支撑表单编辑、父子表、批量操作和更复杂的 AI 表格功能。