rc-form之最纯真状况

媒介

第一次探讨这个框架,关于内里许多逻辑是不懂的,所以只能一点一点去琢磨,个中做了什么。
而进修过程当中,老是不由得猎奇这里的逻辑是干什么的,那边的逻辑是什么的,在不理解这段逻辑是做什么的情况下,死磕很轻易事半功倍。所以本次先从一个比较简朴的场景入手,看看它的源码中做了什么四肢,至于有些逻辑没有涉及到的,先不去管它就好了。

探讨内容

效果

起首上图,看看此次案例的效果。

《rc-form之最纯真状况》

实际上是上一篇案例的精简版,去掉了非空考证。然则剖析的更仔细些。

营业代码

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

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

  componentWillMount() {
    this.nameDecorator = this.props.form.getFieldDecorator('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} style={{padding: '200px'}}>
        {this.nameDecorator(
          <input
            onChange={this.onChange}
          />
        )}
        <div style={{ color: 'red' }}>
          {(getFieldError('name') || []).join(', ')}
        </div>
        <button>Submit</button>
      </form>
    );
  }
}

const WrappedForm = createForm()(Form);
export default WrappedForm;

源码剖析

PS: 源码剖析以代码+备注的情势展现

WrappedForm

概述

这个页面直接衬着了WrappedForm,所以我们无妨直接从WrappedForm看起。
个中WrappedForm是由rc-form供应的createForm建立的,第一个设置对象未通报,第二个参数是要润饰的组件。这里通报给了我们的营业组件

源码

createForm.js

import createBaseForm from './createBaseForm';

// 一系列给其他组件用的自定义混入
export const mixin = {
  getForm() {
    // 这里须要注重的是this是动态的,所以这里的this.xxx会依据环境转变而转变
    return {
      getFieldsValue: this.fieldsStore.getFieldsValue,
      getFieldValue: this.fieldsStore.getFieldValue,
      getFieldInstance: this.getFieldInstance,
      setFieldsValue: this.setFieldsValue,
      setFields: this.setFields,
      setFieldsInitialValue: this.fieldsStore.setFieldsInitialValue,
      getFieldDecorator: this.getFieldDecorator,
      getFieldProps: this.getFieldProps,
      getFieldsError: this.fieldsStore.getFieldsError,
      getFieldError: this.fieldsStore.getFieldError,
      isFieldValidating: this.fieldsStore.isFieldValidating,
      isFieldsValidating: this.fieldsStore.isFieldsValidating,
      isFieldsTouched: this.fieldsStore.isFieldsTouched,
      isFieldTouched: this.fieldsStore.isFieldTouched,
      isSubmitting: this.isSubmitting,
      submit: this.submit,
      validateFields: this.validateFields,
      resetFields: this.resetFields,
    };
  },
};

function createForm(options) {
  // 这里挪用了createBaseForm并将混入传入到该函数
  return createBaseForm(options, [mixin]);
}

export default createForm;

这里我们看到,实在createForm自身没做什么事变,只是在该文件内定义了混入的花样。
接下来我们的重点是createBaseForm中做了什么。这里只列出跟我们案例相干的内容

createBaseForm.js

//团体构造
function createBaseForm(options={}, mixins={} ) {
    const {
        mapPropsToFields,
        onFieldsChange,
        // ****其他的optinos内里的值,下文可能会用到
    } = option;
    //此处的WrappedComponent就是我们示例中,被包裹的Form组件
    return funciton decorate(WrappedComponent) {
        const Form = createReactClass({
            // 该混入包含一个getForm要领用来获得一些通用要领
            mixins,
            getInitialState() {/*mark-init,初始化组件state*/}
            componentWillReceiveProps(nextProps) {/*mark-recProps,初始化部份数据*/}
            onCollect(){/*mark-collect网络表单数据*/}
            onCollectCommon() {}
            getCacheBind() {/*mark-bind组件事宜绑定等网络*/}
            getFieldDecorator() {/*mark-deco装潢组件,增进双向绑定的润饰器*/}
            getFieldProps() {/*mark-props设置字段元数据,盘算被润饰组件的属性*/}
            // 一些其他函数
            
            render() {
                const { wrappedComponentRef, ...restProps } = this.props;
                const formProps = {
                  [formPropName]: this.getForm(),
                };
                // ** 精简本次剖析无关的代码
                // 个中mapProps函数就是一个function(obj) {return obj};
                // 这里用了一个小技能,就是call(this,xxx),直接将该组件上的中心要领,全都放到了子组件的属性上,而且因为该组件是createReactClass建立的,所以子组件(本例中的Form)挪用这些从父组件猎取的要领时,要领内部的this,指向当前组件。
                const props = mapProps.call(this, {
                  ...formProps,
                  ...restProps,
                });
                return <WrappedComponent {...props}/>;
              },
          },
        })
        //简化静态要领转移部份
        return Form;
    }
}

当createBaseForm函数在衬着函数中返回了我们的Form组件后,就能够看到Form组件中做的事变。

Form.js

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

  componentWillMount() {
    // 上个文件,createBaseForm的装潢器函数decorate,天生的组件中衬着了该组件,
    // 并将getFieldDecorator要领经由过程属性通报给了它。
    this.nameDecorator = this.props.form.getFieldDecorator('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} style={{padding: '200px'}}>
        {this.nameDecorator(
          <input
            onChange={this.onChange}
          />
        )}
        {/*这里只是展现错误信息,不是我们关注点*/}
        <div style={{ color: 'red' }}>
          {(getFieldError('name') || []).join(', ')}
        </div>
        <button>Submit</button>
      </form>
    );
  }
}

重点了。症结是看看getFieldDecorator中做了什么。

 getFieldDecorator(name, fieldOption) {
    // 猎取须要通报给被润饰元素的属性。包含onChange,value等
    // 同时在该props中设定用于网络元素值得监听事宜(onChange),以便后续做双向数据。
    const props = this.getFieldProps(name, fieldOption);
    // 经由过程该函数传入(input/被润饰)元素。
    return (fieldElem) => {
      // 此处fieldStore存储字段数据信息以及元数据信息。
      // 数据信息包含value,errors,dirty等
      // 元数据信息包含initValue,defaultValue,校验划定规矩等。
      const fieldMeta = this.fieldsStore.getFieldMeta(name);
      // 猎取input上自身绑定的属性,比方该例子中的onChange打印内容函数
      const originalProps = fieldElem.props;
      fieldMeta.originalProps = originalProps;
      fieldMeta.ref = fieldElem.ref;
      return React.cloneElement(fieldElem, {
        ...props,
        ...this.fieldsStore.getFieldValuePropValue(fieldMeta),
      });
    };
  },

个中getFieldProps值得我们关注

// 简化后的内容以下
getFieldProps(name, usersFieldOption = {}) {
    // name为我们为该文本框起的name
    // 这里的数据fieldOption用来初始背面的FieldMeta。
    const fieldOption = {
      name,
      trigger: 'onChange',
      valuePropName: 'value', // checkBox取值时经由过程checked属性。
      ...usersFieldOption,
    };

    const {
      trigger,
      validateTrigger = trigger,
    } = fieldOption;
    
    // 第一次get元数据平常得不到,内部会返回个空对象
    const fieldMeta = this.fieldsStore.getFieldMeta(name);
    if ('initialValue' in fieldOption) {
      fieldMeta.initialValue = fieldOption.initialValue;
    }
    
    // 这里的inputProps简化后效果为{value: xxx},第一次为空。
    const inputProps = {
      ...this.fieldsStore.getFieldValuePropValue(fieldOption),
    };
    
    // make sure that the value will be collect
    // 这里用来在getFieldDecorator第二个参数为空时,确保给input绑定一个基础的onChange事宜来网络input的修改值,终究放入fieldsStore中
    if (trigger && validateTriggers.indexOf(trigger) === -1) {
      inputProps[trigger] = this.getCacheBind(name, trigger, this.onCollect);
    }
    // 这里就是我们fieldsStore中设置的元数据
    const meta = {
      ...fieldMeta,
      ...fieldOption,
    };
    this.fieldsStore.setFieldMeta(name, meta);
    // 这里返回的{value: 'xxx', onChange: fn};
    return inputProps;
  },

上述代码中onChange绑定了一个onCollect,个中getCacheBind函数主如果修改函数运用时刻this指针。此处疏忽直接剖析onCollect

  onCollect(name_, action, ...args) {
    // 经由过程onCollectCommon在input的onChange中触发,网络到该元素相干东西
    const { name, field, fieldMeta } = this.onCollectCommon(name_, action, args);
    const { validate } = fieldMeta;
    const newField = {
      ...field,
      dirty: hasRules(validate),//依据划定规矩考证,此处可疏忽
    };
    // 更新fieldStore中的值,重要触发了一个forceUpdate要领,从新衬着该组件
    this.setFields({
      [name]: newField,
    });
  },

末了症结的一步就是看看onCollectCommon做了什么

onCollectCommon(name, action, args) {
        const fieldMeta = this.fieldsStore.getFieldMeta(name);
        if (fieldMeta[action]) {
          fieldMeta[action](...args);
        } else if (fieldMeta.originalProps && fieldMeta.originalProps[action]) {
          // 此处挪用input本来的onChange事宜,即打印输入值,这个originalProps是在getFieldDecorator函数中存下的,这里只不过拿来用了。
          fieldMeta.originalProps[action](...args);
        }
        // 此处的getValueFromEvent实在就是取e.target.value.
        const value = fieldMeta.getValueFromEvent ?
          fieldMeta.getValueFromEvent(...args) :
          getValueFromEvent(...args);

        const field = this.fieldsStore.getField(name);
        return ({ name, field: { ...field, value, touched: true }, fieldMeta });
      },

至此全部数据流程基础跑通,onChange触发onCollect去转变fieldStore中的值并forceUpdate更新界面,onCollectCommon则展现了onCollect取值的细节。forceUpdate更新组件后,触发Formrender要领,又最先了之前getFieldDecorator 中读取fieldStore中值,返回被修改后的组件的流程。

题外话

跑通了最简朴的场景,就能够向下一步更庞杂的场景探讨了。

结语

至此一个最简朴的流程已剖析终了。接下来,须要斟酌的就是表单考证,数据反显(从后端拿到数据衬着编辑页面)等等,一步一个脚印,逐步来吧。

    原文作者:A_大白
    原文地址: https://segmentfault.com/a/1190000015498634
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞