React 根据状态动态化功能的一些思考

吾辈的博客原文在:https://blog.rxliuli.com/p/12...,欢迎来玩!

场景

之前吾辈也在 SF 上询问过 类似的问题

目前在实际业务中遇到了两种情况

  1. 程序的功能在分发给不同组织使用的时候有所差异,即不同的组织都会存在一些定制化的功能。
    最常见的差异例如

    • 表单的字段存在差异
    • 列表展示的字段与相关操作有所不同
  2. 组件内的代码在某个权限下才会执行,但是又依赖于组件内的一些状态,如何将这些代码分割到不同的地方(例如不同的文件)便于之后的维护。

    • 一些按钮在指定权限下存在
    • 一些数据在指定权限下展示

方案

  • 使用动态配置渲染不同的页面(可序列化的配置)
  • 根据状态匹配不同的动态组件
  • 使用 hooks 封装不同的逻辑
  • 使用状态图控制状态和逻辑

实际调研结果

使用动态配置渲染不同的页面

实际上,之前有看过吾辈写的 react 通用列表组件封装 就知道,实际上列表已经被配置化了,可以使用配置的形式去渲染一个完整的列表页面,因此可以根据不同的组织使用不同的配置就好了。但是,事实上并没有这么简单,因为就算是简单的列表,也仍然包含上下文,而这,正是配置不能拿到的内容。

上下文主要包括

  • 需要异步请求的数据,例如下拉框的选择项
  • 需要从路由上获取的数据,例如搜索条件
  • 需要对页面内的其它组件进行操作时,例如点击按钮有个新增列表项的弹窗

可以有几种解决方案

  • 通过函数,而不是单纯的配置,这样,可以通过参数解决一些上下文的依赖情况
  • 通过函数且异步,可以解决 api 请求时,此时的 api 必定是可以用的,但是会依赖于 api。

但这仍然会带来问题

  • 数据不再纯粹,无法序列化。
  • 不同配置依赖的数据可能不同,需要配置自己去解决,那么如果这样想的话,那么配置就需要自行获取数据,而不是外部传递数据了
  • 仍然无法使用状态
  • 最重要的是,使用函数之后变得不再像是配置

根据状态匹配不同的动态组件

  • 配置更为灵活,能够获取到组件的上下文
  • 接口请求也没有问题
  • 对不同配置,可以自行对数据进行处理

问题

  • 无法如同纯数据配置那样,复用逻辑这么彻底,但是也可以通过 hooks 解决。
  • UI 复用问题
    先使用组件的方式编写一下,看具体结果如何
  • 无法序列化也意味着无法放到后端,甚至意味着很难做动态加载

使用

  • 使用一个 wrapper 组件来讲 UI 和通用逻辑给包裹进去
  • 使用另外一套组件去区分不同租户的配置(因为是在组件内部写配置,所以该配置可以灵活的使用任意接口,组件上下文可能还不太行)也就是用多个组件来解决这个问题。

可以再尝试一下有没有解决方案。

使用 hooks 封装不同的代码

  • 相比于处理 是哪一个,更适合处理 有或没有 的代码分割
  • 能够使用 react 的状态

问题

  • 使用 hooks 必须放在函数组件最顶层,导致本质上无法 lazy 加载。参考:Hook 规则
  • 使用 hooks 同样难以序列化存储到后端

使用状态图控制状态和逻辑

使用 hooks 封装代码最适合处理元素级的权限控制,但在面对需要根据多个维度的状态决定程序的状态或行为时,就有点力不从心了。而这,也是为什么有限状态机为什么有用的原因。

结论

最终,我们选择了最灵活的 动态组件 + Hooks 共享逻辑 的形式,虽然使用动态组件会增加一些冗余度,但也可以通过子组件或 hooks 的形式复用逻辑,实际上在工程化减小的复杂度的收益是要高于代码冗余的。

使用示例

登记相关内容已经使用该方式进行了重构
  • src/pages/register

    • common: 通用的一些组件和逻辑,例如请求后台接口应该是统一的,但返回的数据类型却应该是单独的

      • form: 表单相关组件,提供给列表/详情页面使用
      • detail: 详情页面
      • list: 列表页面
    • organizations: 不同组织的目录

      • org1: 组织 1
      • org2: 组织 2

吾辈编写了一个简单的示例,代码在 dynamic_state

简单示例

其他技术问题

  • [x] 如何在运行时根据组织切换功能

    • 可以再包一层组件而非简单的从 lazy component map 取出组件
  • [x] 如何在运行时添加新组织的功能

    • 可能需要插件的实现方式,支持动态加载进来,例如 vscode 的插件体系。
  • [x] 如何使用 hooks 更好的复用逻辑

    • 使用 hooks 封装逻辑,使用小型组件封装 UI/UX
  • [x] 如何在打包阶段干掉不相关组织的代码

    • 需要修改 webpack 相关的内容,目前不予考虑
阅读 88

推荐阅读