组件库项目(二):典型组件和 hook
usePermission
之前在项目里封装过一个自定义 Hook —— usePermission。这个 Hook 给我印象特别深,因为它解决了 B 端项目里三个很典型、但又很容易变得混乱的问题:按钮权限获取 → 数据格式兼容 → modal 状态初始化。
B 端列表页中,每个页面都需要调用一次 listAction 接口来获取当前页面允许的操作权限,然后根据返回的权限去渲染按钮。但后端返回的数据格式并不统一,有时是数组,有时是对象,同时每个页面还要维护很多 modal 的开关状态。如果这些逻辑都堆在页面组件里,不仅会非常杂乱,而且会有大量重复代码。所以我把这一系列逻辑抽离成了一个自定义 Hook,让页面只关注 UI。
我把 Hooks 拆成了几个重点功能:
- 权限状态管理:设置了一个 的 state 变量 permission 用于维护权限状态数组
- 请求状态管理/错误信息处理:
- 权限数据 merge:通过传入 shouldMergeKey,将对象类型的权限按需合并成权限按钮列表,解决接口格式不统一导致的兼容问题
- 请求权限的主函数:getPermission
- 把权限转成 modal 初始状态:turnPermissionToInitModalState
这个 usePermission 让我对“如何抽象业务逻辑”和“如何设计可复用 Hook”有了更深的理解。 它把权限逻辑、loading、错误处理、modal 初始化从页面组件中抽离出来,大幅提升了代码整洁度、可维护性和复用率。
然而在将这个 Hook 沉淀到组件库的时候发现了一些问题:
- 原来的 hook 里是直接写死 user.listAction 的,对于组件库来说,不同产品权限 API 可能会不一样
- 某种程度上这个 Hook 违背了职责单一化原则,虽然名字叫 usePermission,但实际上他的职责除了权限管理还包括了弹窗状态初始化、权限合并
所以在下沉到组件库的时候,因为弹窗初始化和权限合并内部并不涉及 state,所以将这两部分逻辑封装成了工具函数放在了 dmp-utils 包,而 Hook 本身只保留“状态管理和请求流程”,实现了单一职责。
而针对问题 1,修改了 props,业务调用方只需要传入 apiService props,一个 resolve 权限数组 promise 对象,最终该 hook 返回{permission,getPermission},在实现功能复用的同时实现了职责单一化。
PermissionButton
封装了一套基于权限控制的按钮组件体系,包括 PermissionButton、PermissionButtonGroup 和表格专用的 PermissionButtonInTable、PermissionButtonGroupInTable。
核心思路是:通过传入的 permission 配置与按钮的 name 进行匹配,并结合 btnRule.show 判断是否渲染。权限不足时组件直接 return null,避免无效渲染,提高性能。组件通过 ...otherProps 支持透传所有属性,完美复用 antd button 的功能。同时组件提供了 customRender 属性,允许业务定制渲染内容,保持可扩展性。
基于这个基础能力,又扩展封装了顶部操作按钮组和表格操作列组件,可以自动过滤没有权限的按钮。这样业务侧不需要再写重复的权限判断逻辑,只需要维护本地的按钮名称数组。