rc-form流程簡析

rc-form-demo:react

import React from 'react';
import ReactDOM from 'react-dom';
import { createForm, formShape } from 'rc-form';

class Form extends React.Component {
  static propTypes = {
    form: formShape,
  };

  componentWillMount() {
    this.nameDecorator = this.props.form.getFieldDecorator('name', {
      initialValue: '',
      rules: [{
        required: true,
        message: 'What\'s your name?', }], }); } onSubmit = (e) => { e.preventDefault(); this.props.form.validateFields((error, values) => { if (!error) { console.log('ok', values); } else { console.log('error', error, values); } }); }; onChange = (e) => { console.log(e.target.value); } render() { const { getFieldError } = this.props.form; return ( <form onSubmit={this.onSubmit}> {this.nameDecorator( <input onChange={this.onChange} /> )} <div style={{ color: 'red' }}> {(getFieldError('name') || []).join(', ')} </div> <button>Submit</button> </form> ); } } const WrappedForm = createForm()(Form); ReactDOM.render(<WrappedForm />, document.getElementById('__react-content')); 複製代碼

上面的例子直接調用了rc-form的createForm方法,第一個參數配置對象未傳遞,第二個參數是須要被修飾的業務組件 在createForm方法中只是定義了mixin,接着繼續調用了最主要的createBaseForm:redux

function createBaseForm(options={}, mixins={} ) {
    const {
        mapPropsToFields, // 頁面初始化或重繪時,將組件接受到的props轉變爲表單項數據注入this.fields中
        onFieldsChange, // 表單項發生改變時執行函數,能夠將表單項的值存入redux.store  
        ...
        withRef, // 設定被封裝組件的ref屬性爲"wrappedComponent" 
    } = option;
    //這裏的WrappenComponent就是一開始例子中的Form組件
    return funciton decorate(WrappedComponent) {
        const Form = createReactClass({
            mixins,
            getInitialState(){}, // 初始化組件state
            componentWillReceiveProps(nextProps){}, // mark-recProps,初始化部分數據
            onCollect(){}, // 收集表單數據
            onCollectCommon() {},
            getCacheBind() {}, // 組件事件綁定等收集
            getFieldDecorator() {}, // 裝飾組件,促進雙向綁定的修飾器
            getFieldProps() {} // 設置字段元數據,計算被修飾組件的屬性
            ...
            
            render() {
                const { wrappedComponentRef, ...restProps } = this.props;
                const formProps = {
                  [formPropName]: this.getForm(), // createForm mixin
                };
          
                // 其中mapProps函數就是一個function(obj) {return obj};
                // 這裏用了一個小技巧,就是call(this,xxx),直接將該組件上的核心方法,全都放到了子組件的屬性上,並且因爲該組件是createReactClass建立的,因此子組件(本例中的Form)調用這些從父組件獲取的方法時,方法內部的this,指向當前組件。
                const props = mapProps.call(this, {
                  ...formProps,
                  ...restProps,
                });
                // 把form屬性掛在到WrappedComponent屬性上
                return <WrappedComponent {...props}/>;
              },
          },
        })
        // 複製包裹組件的靜態屬性到Form上
        return argumentContainer(Form, WrappedComponent);
    }
}
複製代碼
  • 產生一個新容器組件Form,內置getFieldDecorator、getFieldProps等屬性和方法
  • 複製被包裹組件的靜態屬性到新的組建中,執行生命週期時間,getInitialState初始化默認的field,默認無
  • 最後返回被注入了Form組件的原始組件

getFieldDecorator

getFieldDecorator(name, fieldOption) {
    // 在getFieldProps中初始化store字段,綁定onChange事件以便後續方便作雙向數據綁定
    const props = this.getFieldProps(name, fieldOption);
    //props: {value: "", ref: ƒ, onChange: ƒ}
    return (fieldElem) => { // 此處傳入被修飾的input元素
      // fieldStore存儲字段數據信息以及元數據信息。
      // 數據信息包括value,errors,dirty等
      // 元數據信息包括initValue,defaultValue,校驗規則等。
      const fieldMeta = this.fieldsStore.getFieldMeta(name);
      // 獲取input上自己綁定的屬性,例如該例子中的onChange打印內容函數
      // originalProps屬性的主要目的存儲被封裝表單項的onChange事件,fieldOption下無同類事件時,執行該事件  
      const originalProps = fieldElem.props;
      fieldMeta.originalProps = originalProps;
      fieldMeta.ref = fieldElem.ref;
      // clone input組件,並注入新的屬性,onchange,value等
      return React.cloneElement(fieldElem, {
        ...props,
        ...this.fieldsStore.getFieldValuePropValue(fieldMeta),
      });
    };
    
  getFieldProps(name, usersFieldOption = {}) {
    const fieldOption = {
        name, // 自定義組件名稱
        trigger: DEFAULT_TRIGGER, // 綁定默認的onChange事件
        valuePropName: 'value', // 默認值是value屬性,checkBox的值爲checked
        validate: [],
        ...usersFieldOption,
    };

    const {
        rules,
        trigger,
        validateTrigger = trigger,
        validate,
    } = fieldOption;

    const fieldMeta = this.fieldsStore.getFieldMeta(name);
    
    const inputProps = {
        ...this.fieldsStore.getFieldValuePropValue(fieldOption),
        ref: this.getCacheBind(name, `${name}__ref`, this.saveRef),
    };

    //轉換成數組格式
    const validateRules = normalizeValidateRules(validate, rules, validateTrigger);
    //validateRules [{"trigger":["onChange"],"rules":[{"required":true,"message":"What's your name?"}]}]
    const validateTriggers = getValidateTriggers(validateRules);
    validateTriggers.forEach((action) => {
        if (inputProps[action]) return;
        //綁定事件
        inputProps[action] = this.getCacheBind(name, action, this.onCollectValidate);
    });

    //FieldMeta值
    const meta = {
        ...fieldMeta,
        ...fieldOption,
        validate: validateRules,
    };

    // 設置FieldMeta
    this.fieldsStore.setFieldMeta(name, meta);
    
    return inputProps;
}
複製代碼

將表單項包裝爲高階組件數組

  • 建立表單信息到fieldsStore
  • 綁定默認的onChange事件
    • 觸發驗證
    • 保存結果到fieldsStore
  • 返回雙向數據綁定的input組件

在上述getFieldProps中onChange綁定了onCollect方法, 當change事件發生時,獲取表單項的改變值,有校驗規則的表單項添加{dirty:true}屬性,調用validateFieldsInternal方法校驗該表單項,將結果保存到fieldsStore,觸發forceUpdate來重繪表單bash

onCollect(name_, action, ...args) {
        const { name, field, fieldMeta } = this.onCollectCommon(name_, action, args);
        const { validate } = fieldMeta;
        const newField = {
          ...field,
          dirty: hasRules(validate),
        };
        this.setFields({
          [name]: newField,
        });
      },
      
複製代碼

Form內部有本身的狀態管理:fieldsStore記錄着全部表單項的信息,經過getFieldDecorator和表單進行雙向綁定。app

onChange觸發onCollect來改變fieldStore中的值並觸發forceUpdate來更新,onCollectCommon方法內部展現了onCollect取值的細節,forceUpdate在更新組件後,觸發render方法,接着又回到一開始getFieldDecorator中獲取fieldStore內的值,返回被修改後的組件的流程。dom

相關文章
相關標籤/搜索