Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于表单的探讨 #1

Open
MuYunyun opened this issue Nov 29, 2018 · 0 comments
Open

关于表单的探讨 #1

MuYunyun opened this issue Nov 29, 2018 · 0 comments

Comments

@MuYunyun
Copy link
Owner

MuYunyun commented Nov 29, 2018

出发点

中后台项目中表单可以说是最重要的一环, 基本上中台的项目都是在书写各种各样的表单。如何提高开发效率是 daform 正在探索和实践的点。

  • 重复性。
  • 动态表单。每次遇到动态表单的需求都是要花大精力进行, 而且与 redux 耦合性较强, 开发体验十分不好。

设计理念

  • 双向绑定
  • 栅格化布局
  • 错误逻辑集中化管理
  • 动态表单的解决方案
  • 不依赖第三方状态管理库
  • 自由搭配第三方 UI 组件库

可以像如下这样使用

import React from 'react'
import { Input } from 'antd' // 可以是其它 UI 库
import { Form, FormItem } from 'daform'

@Form()
class Demo1 extends React.Component {
  render() {
    return (
      <>
        <FormItem name="name" label="姓名"><Input /></FormItem>
        <FormItem name="age" label="年龄"><Input /></FormItem>
      </>
    )
  }
}

表单静默刷新 vs 双向绑定

如果表单间的数据无需联动, 差不多就是表单数据显示 => 全局对象的单向映射的模型, 这样子只要处理好这一方向的流动性就好了。但是一旦表单间的数据需要联动, 这就涉及到表单 <=> 全局对象的双向映射了(表单间某一数据发生变动通知全局对象, 全局对象改变另一数据变化, 呈现到表单对应数据上)。需要一种机制来在当前的 React 表单中实现双向绑定。借助 React 16.3 出来的 Context 机制, 实现了一个伪双向绑定的表单。

双向绑定

目前表单中修改的数据已经实时同步到内部维护的 formData 对象中, 但假若要使用 setFormItem(itemName, value) 向 formData 写入或修改数据后, 页面应当进行重新渲染, 这时候可以考虑使用观察者模式进行处理。但这个 api 当下的需求用到其实不是特别多, 这部分可以日后实现。

目前用 initialValue 能开发大部分需求。

关于 Context 使用

每当Provider的值发送改变时, 作为Provider后代的所有Consumers都会重新渲染。 从Provider到其后代的Consumers传播不受shouldComponentUpdate方法的约束,因此即使祖先组件退出更新时,后代Consumer也会被更新。

因为表单有联动的需求, 所以重绘表单是必要的。但是处于性能的考虑只重新渲染有变动的表单是十分必要的。结合 Context 的机制, 选择了在 Context 下面使用 PureComponent。

避免表单多次重绘

因为使用 React.cloneElement() 创建 React 对象传入子组件, 所以导致子组件每次都会重新渲染, 因此使用单例模式在内存里将创建的 React 对象进行缓存, 从而避免了多次重绘。目前的设计是仅当 disable 属性发生变化时, 才会再次调用 React.cloneElement()

栅格化布局

借助 sass/less 的语法能动态快速生成 24 栅格化布局。如下以 less 为例生成相应的布局样式

.generate-columns(24);
.generate-columns(@n, @i: 1) when (@i =< @n) {
  .col-@{i} {
    width: (@i * 100% / @n);
  }
  .generate-columns(@n, (@i + 1));
}

错误逻辑集中化管理

错误捕获使用了 async-validator, 个人倾向于将一个表单的错误逻辑放在一个文件里进行管理。

表单存在的状态形式

难免可视状态和编辑状态的字段是会有些微小的地方存在不一致, 基于这样的思考🤔, 暂时将表单的状态只分为编辑态禁用态

动态表单

提供一个动态表单组件 <Dynamic>

import React from 'react'
import { Input } from 'antd'
import { Form, FormItem } from 'daform'

@Form()
class Demo1 extends React.Component {
  render() {
    const { form } = this.props
    return (
      <Dynamic>
        <FormItem name="name" label="姓名"><Input /></FormItem>
        <FormItem name="age" label="年龄"><Input /></FormItem>
      </Dynamic>
    )
  }
}

表单里面所有数据结构(包括动态表单)都是扁平化的, 动态表单数据结构类似:

{
  "name0": "deku",
  "age0": "12",
  "name1": "siren",
  "age1": "13",
  "name2": "diana",
  "age2": "11"
}

测试用例

框架选择了 Jest + Enzyme;

测试应该覆盖到的几个点:

  • Form
    • Form 中的 data
  • FormItem
    • 传入 FormItem 的各个属性的校验
    • 渲染次数的校验
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant