中后台领域中,数据录入是一个重要的场景,在该场景中form(表单)扮演一个重要的角色。表单中涉及到大量的交互,主要表现在以下几个方面:

  • 收集用户输入(input, textarea等)
  • 事件处理(onChange等)
  • 联动(数据同步,异步)
  • 数据校验,提交
  • 我们从一个简单登录页的例子说起,来总结form是如何使用的。

    react原生实现

    我们直接使用react的受控组件模式,对每个输入项的状态保存在组件state中,通过onChange事件,实时更新值。

    import React from 'react';
    class FormDemo extends React.Component {
        state = {
            username: '',
            password: '',
            usernameMsg: '',
            passwordMsg: '',
        onChangeName = e => {
            const value = e.target.value;
            this.setState({
                username: value,
                usernameMsg: !value ? '请填写' : ‘’,  // 非空校验
        onChangePassword = e => {
            const value = e.target.value;
            this.setState({
                password: value,
                passwordMsg: !value ? '请填写' : ‘',  // 非空校验
        handleSubmit = () => {
            // post data
        render() {
            // 获取数据和错误信息
            const { username, password, usernameMsg, passwordMsg } = this.state;
            return (
                    <input value={username} onChange={this.onChangeName} />
                    <span>{usernameMsg}</span>
                    <input value={password} onChange={this.onChangePassword} />
                    <span>{passwordMsg}</span>
                    <button type="submit" onClick={this.handleSubmit}>提交</button>
                </form>
    复制代码

    这种方法百分之百使用原生的方式实现:表单的每一个field都对应于组件内的state的一个值,每一个field有一个对应的错误信息用于展示错误,每个field的值通过onChange事件进行改变。这种方式实现简单明了,但是当field较多时,需要大量的重复这种 value + onChange 的模式。所以,需要一种方式,将重复的工作抽象出来。

    rc-form,antd实现

    rc-form内部使用fieldsStore对所有field进行集中式管理,当数据改变时,重绘整个form。

    import { createForm } from 'rc-form';
    class Form extends React.Component {
      submit = () => {
        this.props.form.validateFields((error, value) => {
          console.log(error, value);
      render() {
        let usernameErrors, passwordErrors;
        const { getFieldProps, getFieldError } = this.props.form;
        return (
            <input {...getFieldProps('username', {rules: [{required: true}]})}/>
            {(usernameErrors = getFieldError('password')) ? errors.join(',') : null}
            <input {...getFieldProps('password', {rules: [{required: true}]})}/>
            {(passwordErrors = getFieldError('password')) ? errors.join(',') : null}
            <button onClick={this.submit}>submit</button>
    export createForm()(Form);
    复制代码

    createForm() 使用高阶组件的方式,对form注入了一些额外的方法与属性。 getFieldProps 方法用于把field注册到fieldsStore里面,fieldsStore对field的变化进行追踪。可以看出相比第一种方式,rc-form把状态与UI进行分层,我们省去了对每一个field的状态管理,不用再去为每一个field进行状态进行 value + onChange 的重复。

    在以上过程中,我们完成了登录的功能逻辑,但是还缺少表单的样式。表单样式包括两部分:表单的整体样式和每个field的样式。于是我们抽象出Form和FormItem用于承载样式,于是就有了antdForm。antd form就是在rc-form的基础上增加了form布局演化而来。我们也可以通过自定义Form和FormItem + rc-form的形式去实现form的布局。

    代码如下:

    <Form onSubmit={(values) => { submit(values) }}>
      <Form.Item label="姓名">
        {getFieldDecorator('name')(<Input />)}
      </Form.Item>
      <Form.Item label="密码">
        {getFieldDecorator('password')(<Input.Password />)}
      </Form.Item>
      <Form.Item>
        <Button type="primary" htmlType="submit">提交</Button>
      </Form.Item>
    </Form>
    复制代码

    antd-form的架构如下:

    uform使用

    在使用antd-form过程中,遇到以下问题:

  • antd-form采用单向数据流的方式管理状态,任何字段变动都会导致fieldsStore的改变进而导致组件的全量渲染,出现性能问题
  • 在实现联动时,联动逻辑分散在各个表单组件的onChange方法中,通过this.props.form.setFieldsValue来处理联动。如果出现很多联动,不得不写很多onChange,导致业务组件变得非常臃肿且分散
  • antdForm中到处都是FormItem组件,到处都是onChange,到处都是{…formItemLayout},重复且低效,导致研发效率低下
  • uform很好的解决了使用form过程中遇到的各种问题,以uform 0.4.x版本为例(1.x版本发生了很大的变化)。

    UForm 主要分为三层结构:

  • @uform/core 层,负责表单内部的数据状态管理,校验管理,副作用逻辑管理
  • @uform/react 层,负责在 React 中集成 UForm,帮助用户快速接入各种 React 组件库
  • 组件库层,属于 @uform/react 的插件包,可以接入各种组件库,比如:Ant Design/Fusion Design
  • uform采用分布式状态管理,数据同步靠根组件广播需要更新子组件重绘,根组件只负责消息分发。这样可以做到只更新单个组件。uform支持json schema和集中性的副作用管理。

    使用uform代码如下:

    const Schema = {
        user: {
            type: 'object',
                properties: {
                    username: {
                        type: 'string',
                        title: '用户名',
                        required: true,
                    password: {
                        type: 'string',
                        title: '密码',
                        required: true,
    const defaultValue = {
        user: {
            username: '',
            password: ''
    <SchemaForm
            labelCol={8}
            wrapperCol={16}
            schema={Schema}
            defaultValue={defaultValue}
            onSubmit={this.onSubmit}
    </SchemaForm>
    复制代码

    使用uform,我们可以先定义form的schema和默认值,之后交给uform进行渲染。 uform可以方便的处理多种场景,如联动等。

  • 私信
    4,608