Form使用总结

背景

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

  • 收集用户输入(input, textarea等)
  • 事件处理(onChange等)
  • 联动(数据同步,异步)
  • 数据校验,提交
  • ...

咱们从一个简单登陆页的例子提及,来总结form是如何使用的。html

登陆

react原生实现

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

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 (
            <form>
                <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的模式。因此,须要一种方式,将重复的工做抽象出来。react

rc-form,antd实现

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

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 (
      <div>
        <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>
      </div>
    );
  }
}

export createForm()(Form);
复制代码

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

在以上过程当中,咱们完成了登陆的功能逻辑,可是还缺乏表单的样式。表单样式包括两部分:表单的总体样式和每一个field的样式。因而咱们抽象出Form和FormItem用于承载样式,因而就有了antdForm。antd form就是在rc-form的基础上增长了form布局演化而来。咱们也能够经过自定义Form和FormItem + rc-form的形式去实现form的布局。架构

代码以下:app

<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的架构以下: 异步

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能够方便的处理多种场景,如联动等。

相关文章
相关标签/搜索