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能夠方便的處理多種場景,如聯動等。

相關文章
相關標籤/搜索