rc-form源码解读 v=1.1.0

1.轴心createBaseForm.js模块:封装WrappedComponent下游组件,为其提供props.form表单赋值、校验工具函数集。

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

// defineProperty(obj,name,value)定义对象的属性
var _defineProperty2 = require('babel-runtime/helpers/defineProperty');
var _defineProperty3 = _interopRequireDefault(_defineProperty2);

// 拷贝
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);

var _react = require('react');
var _react2 = _interopRequireDefault(_react);

var _utils = require('./utils');

var _asyncValidator = require('async-validator');
var _asyncValidator2 = _interopRequireDefault(_asyncValidator);

var _warning = require('warning');
var _warning2 = _interopRequireDefault(_warning);

var _lodash = require('lodash.get');
var _lodash2 = _interopRequireDefault(_lodash);

var _lodash3 = require('lodash.has');
var _lodash4 = _interopRequireDefault(_lodash3);

var _lodash5 = require('lodash.set');
var _lodash6 = _interopRequireDefault(_lodash5);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

var DEFAULT_VALIDATE_TRIGGER = 'onChange';
var DEFAULT_TRIGGER = DEFAULT_VALIDATE_TRIGGER;
var atom = {};

// createBaseForm({
//    mapPropsToFields,// 页面初始化或重绘时,将组件接受到的props转变为表单项数据注入this.fields中
//    onFieldsChange,// 表单项发生改变时执行函数,可以将表单项的值存入redux.store
//    fieldNameProp,// 设置表单项存储name的键
//    fieldMetaProp,// 用于getFieldProps添加的数据转化后挂载到元素的props,提供键
//    validateMessages,// 设置表单的错误文案
//    mapProps,// render方法渲染时对组件的props进行处理
//    formPropName,// 被包装组件获取getFieldProps方法的接口名,默认为this.props.form
//    withRef,// 设定被封装组件的ref属性为"wrappedComponent"
// },mixins)
function createBaseForm() {
  var option = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var mixins = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  var mapPropsToFields = option.mapPropsToFields,// 页面初始化或重绘时,将组件接受到的props转变为表单项数据注入this.fields中
      onFieldsChange = option.onFieldsChange,// 表单项发生改变时执行函数,可以将表单项的值存入redux.store
      fieldNameProp = option.fieldNameProp,// 设置表单项存储name的键
      fieldMetaProp = option.fieldMetaProp,// 用于getFieldProps添加的数据转化后挂载到元素的props,提供键
      validateMessages = option.validateMessages,// 设置表单的错误文案
      _option$mapProps = option.mapProps,// render方法渲染时对被封装组件的props进行处理
      mapProps = _option$mapProps === undefined ? _utils.mirror : _option$mapProps,
      _option$formPropName = option.formPropName,// 被包装组件获取getFieldProps方法的接口名,默认为this.props.form
      formPropName = _option$formPropName === undefined ? 'form' : _option$formPropName,
      withRef = option.withRef;// 设定被封装组件的ref属性为"wrappedComponent"

  // 封装高阶组件,为下游WrappedComponent组件注入this.props.form工具函数集等
  function decorate(WrappedComponent) {
    var Form = _react2["default"].createClass({
      displayName: 'Form',

      mixins: mixins,

      getInitialState: function getInitialState() {
        var fields = void 0;
        if (mapPropsToFields) {
          fields = mapPropsToFields(this.props);
        }
        this.fields = fields || {};// 存储表单项的值,错误文案等即时数据,重绘表单时props从this.fields取值
        this.instances = {};
        this.fieldsMeta = {};// 存储getFieldProps、getFieldDecorator方法经过数据处理后的原始配置值,{name:options}形式
        this.cachedBind = {};// 存储绑点事件相关onChange、onChangeValidate、以及删除数据相关this.saveRef方法
        return {
          submitting: false
        };
      },

      // 表单重绘时,通过mapPropsToFields从props中获取数据注入this.fields
      componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
        if (mapPropsToFields) {
          this.fields = mapPropsToFields(nextProps);
        }
      },

      // change事件发生时,获取表单项的改变值,有校验规则的表单项添加{dirty:true}属性,调用setFields方法重绘表单
      onChange: function onChange(name_, action) {
        var name = name_;
        var fieldMeta = this.getFieldMeta(name);
        var validate = fieldMeta.validate;

        // react注入的事件参数,如event等
        for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
          args[_key - 2] = arguments[_key];
        }

        // 调用getFieldProps(name,{onChange:functaion(){}})方式添加的onChange方法
        if (fieldMeta[action]) {
          fieldMeta[action].apply(fieldMeta, args);

        // 调用getFieldDecorator(name,fieldOption)(<Input onChange={function(){}}/>)添加的onChange方法
        // fieldMeta.originalProps属性在调用getFieldDecorator方法封装表单项时会产生
        } else if (fieldMeta.originalProps && fieldMeta.originalProps[action]) {
          var _fieldMeta$originalPr;

          (_fieldMeta$originalPr = fieldMeta.originalProps)[action].apply(_fieldMeta$originalPr, args);
        }

        // _utils.getValueFromEvent(e)获取表单项的即时值,即e.target.value | checked 
        var value = fieldMeta.getValueFromEvent ? fieldMeta.getValueFromEvent.apply(fieldMeta, args) : _utils.getValueFromEvent.apply(undefined, args);
        
        var fieldContent = void 0;
        var nameKeyObj = (0, _utils.getNameIfNested)(name);
        if (this.getFieldMeta(nameKeyObj.name).exclusive) {
          name = nameKeyObj.name;
        }

        var field = this.getField(name);
        fieldContent = (0, _extends3["default"])({}, field, {
          value: value,
          dirty: (0, _utils.hasRules)(validate)// 有校验规则时dirty属性赋值为true
        });

        // this.fields重新赋值,并调用this.forceUpdate重绘页面
        this.setFields((0, _defineProperty3["default"])({}, name, fieldContent));
      },

      // change事件发生时,获取表单项的改变值,表单项添加{dirty:true}属性,调用validateFieldsInternal方法校验该表单项
      onChangeValidate: function onChangeValidate(name_, action) {
        var name = name_;
        var fieldMeta = this.getFieldMeta(name);

        // react注入的事件参数,如event等
        for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
          args[_key2 - 2] = arguments[_key2];
        }

        // 调用getFieldProps(name,{onChange:functaion(){}})方式添加的onChange方法
        if (fieldMeta[action]) {
          fieldMeta[action].apply(fieldMeta, args);

        // 调用getFieldDecorator(name,fieldOption)(<Input onChange={function(){}}/>)添加的onChange方法
        // fieldMeta.originalProps属性在调用getFieldDecorator方法封装表单项时会产生
        } else if (fieldMeta.originalProps && fieldMeta.originalProps[action]) {
          var _fieldMeta$originalPr2;

          (_fieldMeta$originalPr2 = fieldMeta.originalProps)[action].apply(_fieldMeta$originalPr2, args);
        }

        // _utils.getValueFromEvent(e)获取表单项的即时值,即e.target.value | checked
        var value = fieldMeta.getValueFromEvent ? fieldMeta.getValueFromEvent.apply(fieldMeta, args) : _utils.getValueFromEvent.apply(undefined, args);
        var nameKeyObj = (0, _utils.getNameIfNested)(name);
        if (this.getFieldMeta(nameKeyObj.name).exclusive) {
          name = nameKeyObj.name;
        }
        var field = this.getField(name);
        field.value = value;
        field.dirty = true;
        this.validateFieldsInternal([field], {
          action: action,
          options: {
            firstFields: !!fieldMeta.validateFirst
          }
        });
      },
      
      // 缓存或获取cache中缓存的校验数据方法onChangeValidate、收集表单项数据的方法onChange、移除数据的方法saveRef
      getCacheBind: function getCacheBind(name, action, fn) {
        var cache = this.cachedBind[name] = this.cachedBind[name] || {};
        if (!cache[action]) {
          cache[action] = fn.bind(this, name, action);
        }
        return cache[action];
      },

      // 获取getFieldProps(name,fieldOption)、getFieldDecorator(name,fieldOption)注入的表单项元数据fieldOption
      // fieldOption经数据处理后
      getFieldMeta: function getFieldMeta(name) {
        return this.fieldsMeta[name];
      },

      // 获取this.fields[name],并添加{name:name}属性后输出
      getField: function getField(name) {
        var fields = this.fields;

        return (0, _extends3["default"])({}, fields[name], {
          name: name
        });
      },

      // {getFieldDecorator(name,fieldOption)(<Input {...props}/>)}将表单项包装为高阶组件后返回
      // 实现功能同getFieldProps方法,内部也调用getFieldProps方法
      // 与getFieldProps方法不同的是,被封装表单项的props作为this.fieldMeta[name]的originalProps属性
      //     originalProps属性的主要目的存储被封装表单项的onChange事件,fieldOption下无同类事件时,执行该事件
      // 不推荐将value、defaultValue作为表单项组件如Input的props属性
      getFieldDecorator: function getFieldDecorator(name, fieldOption) {
        var _this = this;

        var props = this.getFieldProps(name, fieldOption);
        return function (fieldElem) {
          var fieldMeta = _this.getFieldMeta(name);
          var originalProps = fieldElem.props;
          if (process.env.NODE_ENV !== 'production') {
            var valuePropName = fieldMeta.valuePropName;
            (0, _warning2["default"])(!(valuePropName in originalProps), '`getFieldDecorator` will override `' + valuePropName + '`, ' + ('so please don\'t set `' + valuePropName + '` directly ') + 'and use `setFieldsValue` to set it.');
            var defaultValuePropName = 'default' + valuePropName[0].toUpperCase() + valuePropName.slice(1);
            (0, _warning2["default"])(!(defaultValuePropName in originalProps), '`' + defaultValuePropName + '` is invalid ' + ('for `getFieldDecorator` will set `' + valuePropName + '`,') + ' please use `option.initialValue` instead.');
          }
          fieldMeta.originalProps = originalProps;
          fieldMeta.ref = fieldElem.ref;
          return _react2["default"].cloneElement(fieldElem, (0, _extends3["default"])({}, props, _this.getFieldValuePropValue(fieldMeta)));
        };
      },

      /*
      getFieldProps(name,{// 待校验节点的唯一标志。支持嵌套式的写法,单层嵌套,不稳定,后续版本中有所调整
        valuePropName,// 待校验节点的值设置,如Switch的是'checked'
        validate,// 校验规则,数据格式为{trigger:"",rules:[{}]}
        rules,// 校验规则rules:[{required:true,message:"name is required."}]
        validate,// 校验时机及校验规则validate:[trigger:"onClick",rules:[{required:true,message:"name is required."}]]
        trigger,// 收集待校验节点的值的时机,默认"onChange"
        validateTrigger,// 校验待校验节点的值的时机,默认"onChange"
        initialValue,// 待校验节点的初始值,类型、可选值均由待校验节点决定
        exclusive,// 设为真值时,获取表单项数据和校验时将排除在外,譬如用于控制input元素显隐的radio、checkbox等;同时name要求是嵌套结构
        initialValue,// 表单项的初始值
        valuePropName,// 定义存储数据的键,同时作为react元素表单项的属性名,用于存储initialValue等,默认为value
        getValueProps(fieldValue),// 传入表单项的值,定义表单项存储该值得属性名后,以对象形式输出
        getValueFromEvent(e),// onChange事件触发时,定义以何种形式获取表单项的即时值,默认为e.target.value || checked
        normalize:function(value,prevValues,nextValues){},// this.setFields方法中用于格式化表单项的值
        onChange:function(){},// 设置表单项值改变时的事件,该数据不会流入inputProps,作为表单项的props属性;但会在this.onChange方法中得到调用
        ref:function(component){},// 设置组件实例ref属性将执行的回调函数,component为该组件实例
      })
      <Input {...getFieldProps(name,fieldOption)}/>
      */
      // 将转化后的数据存入this.fieldsMeta中,校验数据以validateRules=[{trigger,rules}]的形式
      //      不包含inputProps中的inputProps[action]、inputProps[fieldOption.trigger],添加到react元素上的绑定事件
      // 添加到元素上的props为getFieldProps(name,{trigger,rules})函数体内的inputProps
      //    包含校验触发方法{[action]:this.onChangeValidate}、收集数据的方法{[trigger]:this.onChange}
      //    通过this.getFieldValuePropValue获取的元素的值{value:initialValue}
      //    用于移除this.fields|fieldsMeta|instances|cachedBind[name]相关数据的方法{ref:this.saveRef}
      //    以及作为元素属性存储的getFieldProps方法次参配置项(经转化后){[options.fieldMetaProp]:meta}
      //        options.fieldMetaProp为ant-design中使用Form.create({fieldMetaProp})传参

      // 调用this.forceUpdate重绘表单时,将调用getFieldProps通过this.fields重新计算表单项的props
      getFieldProps: function getFieldProps(name) {
        var _this2 = this;

        var fieldOption = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

        if (!name) {
          throw new Error('Must call `getFieldProps` with valid name string!');
        }

        // fieldOption数据转化
        fieldOption.valuePropName = fieldOption.valuePropName || 'value';
        fieldOption.validate = fieldOption.validate || [];

        var rules = fieldOption.rules,
            _fieldOption$trigger = fieldOption.trigger,
            trigger = _fieldOption$trigger === undefined ? DEFAULT_TRIGGER : _fieldOption$trigger,
            exclusive = fieldOption.exclusive,
            _fieldOption$validate = fieldOption.validateTrigger,
            validateTrigger = _fieldOption$validate === undefined ? DEFAULT_VALIDATE_TRIGGER : _fieldOption$validate,
            validate = fieldOption.validate;

        fieldOption.trigger = trigger;
        fieldOption.validateTrigger = validateTrigger;

        // name设置为"key1.key2"形式或"key1[key2]"形式,取得nameIfNested为{name:"key1",isNested:true}
        // name不是嵌套形式如"key",返回{name:"key"}
        // 示例:
        // 设置嵌套结构为:
        //     {getFieldDecorator('member[0].name.firstname', {})(<input/>)}
        //     {getFieldDecorator('member[1].name.firstname', {})(<input/>)}
        // getFieldsValue方法返回数据为:members: [{ name: {firstname: "xxx"} }, { name: {firstname: "yyy"} }]
        var nameIfNested = (0, _utils.getNameIfNested)(name);
        var leadingName = nameIfNested.name;// name为嵌套结构如"key1.key2"的父键"key1"

        fieldOption.leadingName = leadingName;
        fieldOption.name = name;

        var fieldsMeta = this.fieldsMeta;

        var fieldMeta = void 0;
        var leadingFieldMeta = fieldsMeta[leadingName];

        if (nameIfNested.isNested) {
          leadingFieldMeta = fieldsMeta[leadingName] = fieldsMeta[leadingName] || {};
          leadingFieldMeta.virtual = !exclusive;
          // exclusive allow getFieldProps('x', {initialValue})
          // non-exclusive does not allow getFieldProps('x', {initialValue})
          leadingFieldMeta.hidden = !exclusive;
          leadingFieldMeta.exclusive = exclusive;
          fieldMeta = fieldsMeta[name] = fieldsMeta[name] || {};
        } else {
          fieldMeta = fieldsMeta[name] = fieldsMeta[name] || {};
        }

        if ('initialValue' in fieldOption) {
          fieldMeta.initialValue = fieldOption.initialValue;
        }

        var inputProps = {};

        if (fieldNameProp) {
          inputProps[fieldNameProp] = name;
        }

        // 将fieldOption.validate、fieldOption.rules并入validateRules中
        var validateRules = validate.map(function (item) {
          var newItem = (0, _extends3["default"])({}, item, {
            trigger: item.trigger || []
          });
          if (typeof newItem.trigger === 'string') {
            newItem.trigger = [newItem.trigger];
          }
          return newItem;
        });

        if (rules) {
          validateRules.push({
            trigger: validateTrigger ? [].concat(validateTrigger) : [],
            rules: rules
          });
        }

        // 将_this2.onChangeValidate.bind(this,name,action);函数存入this.cachedBind[name][action]
        // inputProps存储{action:this.cachedBind[name][action]}
        // 其中action如"onChange",name为待校验节点的唯一标识
        // this.onChangeValidate方法用于校验节点
        validateRules.filter(function (item) {
          return !!item.rules && item.rules.length;
        }).map(function (item) {
          return item.trigger;
        }).reduce(function (pre, curr) {
          return pre.concat(curr);
        }, []).forEach(function (action) {
          // 将_this2.onChangeValidate.bind(this,name,action)函数存入this.cachedBind[name][action]
          inputProps[action] = _this2.getCacheBind(name, action, _this2.onChangeValidate);
        });

        function checkRule(item) {
          return item.trigger.indexOf(trigger) === -1 || !item.rules || !item.rules.length;
        }

        // 校验validateRules数组各元素项的trigger是否不包含fieldOption.trigger及rules是否存在
        // 通过校验,将this.onChange.bind(this,name,trigger)存入this.cachedBind[name][trigger]
        // inputProps存储{trigger:this.cachedBind[name][trigger]}
        // this.onChange方法用于收集数据
        if (trigger && validateRules.every(checkRule)) {
          inputProps[trigger] = this.getCacheBind(name, trigger, this.onChange);
        }

        // getFieldValuePropValue方法以键值对形式获取this.fields[name].value表单项的实时值
        //     或fieldOption.initialValue初始值
        //     键为fieldOption.valuePropName,默认为"value",最终作为表单项的props属性
        inputProps = (0, _extends3["default"])({}, inputProps, this.getFieldValuePropValue(fieldOption));

        // 将this.saveRef.bind(this,name,name+'__ref')存入this.cachedBind[name][name+'__ref']
        // 存储的函数用于移除this.fields | fieldsMeta | instances | cachedBind中存储的相关数据
        inputProps.ref = this.getCacheBind(name, name + '__ref', this.saveRef);

        var meta = (0, _extends3["default"])({}, fieldMeta, fieldOption, {
          validate: validateRules
        });

        // 将转化后的数据存入this.fieldsMeta中,校验数据以validateRules=[{trigger,rules}]的形式
        //      不包含inputProps中的inputProps[action]、inputProps[trigger],添加到react元素上的绑定事件
        fieldsMeta[name] = meta;

        // ant-design中使用Form.create({fieldMetaProp}),fieldMetaProp作为将getFieldProps添加的数据处理后挂载的键
        if (fieldMetaProp) {
          inputProps[fieldMetaProp] = meta;
        }

        return inputProps;
      },

      // 传参fieldMeta为getFieldProps(name,fieldOption)方法中经数据处理后的fieldOption
      // 以键值对形式获取this.fields[name].value表单项的实时值或fieldOption.initialValue初始值
      //     键为fieldOption.valuePropName,默认为"value",最终作为表单项的props属性
      getFieldValuePropValue: function getFieldValuePropValue(fieldMeta) {
        var exclusive = fieldMeta.exclusive,
            leadingName = fieldMeta.leadingName,
            name = fieldMeta.name,
            getValueProps = fieldMeta.getValueProps,
            valuePropName = fieldMeta.valuePropName;
        var fieldsMeta = this.fieldsMeta;

        // 获取this.fields[name],形式为{name:name}
        var field = exclusive ? this.getField(leadingName) : this.getField(name);
        var fieldValue = atom;

        // 预先获取调用this.setFields方法强制向this.fields[name]表单项注入的值
        if (field && 'value' in field) {
          fieldValue = field.value;
        }

        // 其次获取getFieldProps(name,{initialValue})方法添加的初始值
        if (fieldValue === atom) {
          fieldValue = exclusive ? fieldsMeta[leadingName].initialValue : fieldMeta.initialValue;
        }

        // 由表单项的值转化为键值对输出
        if (getValueProps) {
          return getValueProps(fieldValue);
        }

        // 转化为键值对{[fieldOption.valuePropName]:fieldValue}形式后输出
        return (0, _defineProperty3["default"])({}, valuePropName, fieldValue);
      },

      // 获取this.fields[name][member]属性数据
      getFieldMember: function getFieldMember(name, member) {
        var field = this.getField(name);
        return field && field[member];
      },

      // 获取this.fields[name]["errors"]错误数据,并剔除没有error.message的错误数据
      getFieldError: function getFieldError(name) {
        return (0, _utils.getErrorStrs)(this.getFieldMember(name, 'errors'));
      },

      // 获取所有表单项的name,getFieldProps(name,{exclusive:true})中exclusive设为真值的除外
      getValidFieldsName: function getValidFieldsName() {
        var fieldsMeta = this.fieldsMeta;
        return fieldsMeta ? Object.keys(fieldsMeta).filter(function (name) {
          return !fieldsMeta[name].hidden;
        }) : [];
      },

      // 遍历names以获得表单项的name,为空时获取this.fieldsMeta下的所有表单项name
      // 接着获取表单项的值this.fields[name].value或者this.fieldsMeta[name].initialValue
      // 特别当name为嵌套结构的父键如"pKey"时,获取该父键下所有子嵌套表单项如pKey.childKey1、pKey.childKey2的值
      getFieldsValue: function getFieldsValue(names) {
        var _this3 = this;

        // utils.flatFieldNames方法,name若为非嵌套式书写"key",获取"key";若为嵌套式书写"key1.key2",获取父键"key1"
        var fields = names || (0, _utils.flatFieldNames)(this.getValidFieldsName());
        var allValues = {};
        fields.forEach(function (f) {
          (0, _lodash6["default"])(allValues, f, _this3.getFieldValue(f));
        });
        return allValues;
      },

      // 获取表单项的值this.fields[name].value或者this.fieldsMeta[name].initialValue
      // 特别当name为嵌套结构的父键如"pKey"时,获取该父键下所有子嵌套表单项如pKey.childKey1、pKey.childKey2的值
      getFieldValue: function getFieldValue(name) {
        var fields = this.fields;

        return this.getValueFromFields(name, fields);
      },

      // 获取表单项组件实例,该实例何时添加到this.instances中???
      getFieldInstance: function getFieldInstance(name) {
        return this.instances[name];
      },

      // 获取表单项的值this.fields[name].value或者this.fieldsMeta[name].initialValue
      getValueFromFieldsInternal: function getValueFromFieldsInternal(name, fields) {
        var field = fields[name];
        if (field && 'value' in field) {
          return field.value;
        }
        var fieldMeta = this.fieldsMeta[name];
        return fieldMeta && fieldMeta.initialValue;
      },

      // 获取表单项的值this.fields[name].value或者this.fieldsMeta[name].initialValue
      // 特别当name为嵌套结构的父键如"pKey"时,获取该父键下所有子嵌套表单项如pKey.childKey1、pKey.childKey2的值
      getValueFromFields: function getValueFromFields(name, fields) {
        var fieldsMeta = this.fieldsMeta;

        // fieldsMeta[name].virtual仅当getFieldProps(name,{exclusive:true})中exclusive为真时,为否值;其余都是真值

        // 当name为嵌套结构的父键时,获取该父键下所有子嵌套表单项的值
        if (fieldsMeta[name] && fieldsMeta[name].virtual) {
          var ret = {};
          for (var fieldKey in fieldsMeta) {
            if (fieldsMeta.hasOwnProperty(fieldKey)) {
              var nameIfNested = (0, _utils.getNameIfNested)(fieldKey);
              if (nameIfNested.name === name && nameIfNested.isNested) {
                (0, _lodash6["default"])(ret, fieldKey, this.getValueFromFieldsInternal(fieldKey, fields));
              }
            }
          }
          return ret[name];
        }
        return this.getValueFromFieldsInternal(name, fields);
      },

      // 通过action事件名获取校验规则
      getRules: function getRules(fieldMeta, action) {
        var actionRules = fieldMeta.validate.filter(function (item) {
          return !action || item.trigger.indexOf(action) >= 0;
        }).map(function (item) {
          return item.rules;
        });
        return (0, _utils.flattenArray)(actionRules);
      },

      // setFields(fields)方法重新计算表单项的value值,赋值给this.fields
      // 并调用用户配置onFieldsChange(props,values)方法,用于将表单数据存入redux.store
      // 最后调用this.forceUpdate重绘表单,重绘表单时必然调用getFieldProps通过this.fields重新计算表单项的props
      setFields: function setFields(fields_) {
        var _this4 = this;

        var fieldsMeta = this.fieldsMeta;
        var fields = fields_;
        var nowFields = (0, _extends3["default"])({}, this.fields, fields);

        // 以对象形式获取待赋值表单项及之前所有表单项的值,getFieldProps(name,{exclusive:true})中exclusive为真值的除外
        var nowValues = {};
        Object.keys(fieldsMeta).forEach(function (f) {
          var _getNameIfNested = (0, _utils.getNameIfNested)(f),
              name = _getNameIfNested.name,
              isNested = _getNameIfNested.isNested;

          if (isNested && fieldsMeta[name].exclusive) {
            return;
          }
          nowValues[f] = _this4.getValueFromFields(f, nowFields);
        });
        var changedFieldsName = Object.keys(fields);

        // getFieldProps(name,{normalize:function(value,prevValues,nextValues){}})中normalize方法格式化表单项的值
        Object.keys(nowValues).forEach(function (f) {
          var value = nowValues[f];
          var fieldMeta = fieldsMeta[f];
          if (fieldMeta && fieldMeta.normalize) {
            var nowValue = fieldMeta.normalize(value, _this4.getValueFromFields(f, _this4.fields), nowValues);
            if (nowValue !== value) {
              nowFields[f] = (0, _extends3["default"])({}, nowFields[f], {
                value: nowValue
              });
            }
          }
        });
        this.fields = nowFields;
        if (onFieldsChange) {
          (function () {
            var changedFields = {};
            changedFieldsName.forEach(function (f) {
              changedFields[f] = _this4.getField(f);
            });
            onFieldsChange(_this4.props, changedFields);
          })();
        }
        this.forceUpdate();
      },

      // 通过键值对设置表单项的值,并调用setFields方法重绘表单页面
      setFieldsValue: function setFieldsValue(fieldsValue) {
        var newFields = {};
        var fieldsMeta = this.fieldsMeta,
            fields = this.fields;

        // utils.getVirtualPaths(fieldsMeta)方法
        // getFieldProps(name,fieldOption)中,name若以嵌套形式设置为"pKey.cKey"
        // 将获得virtualPaths为{pKey:"pKey.cKey"}
        // 所有name均为非嵌套形式"key",将获得virtualPaths为{}
        var virtualPaths = (0, _utils.getVirtualPaths)(fieldsMeta);
        for (var name in fieldsValue) {
          if (fieldsValue.hasOwnProperty(name)) {
            var value = fieldsValue[name];

            // getFieldProps(name,{exclusive:false})中name设定为嵌套形式,且exclusive为否值(默认为否值)时
            //     fieldsMeta[name].virtual为真值
            // name为嵌套形式的父键,获取该键下所有嵌套属性对应表单项的值
            if (fieldsMeta[name] && fieldsMeta[name].virtual) {

              // utils.clearVirtualField用于删除fields中name不是嵌套结构的键
              (0, _utils.clearVirtualField)(name, fields, fieldsMeta);
              for (var i = 0, len = virtualPaths[name].length; i < len; i++) {
                var path = virtualPaths[name][i];
                if ((0, _lodash4["default"])(fieldsValue, path)) {
                  newFields[path] = {
                    name: path,
                    value: (0, _lodash2["default"])(fieldsValue, path)
                  };
                }
              }
            // name非嵌套形式,获取表单项的值
            } else if (fieldsMeta[name]) {
              newFields[name] = {
                name: name,
                value: value
              };
            }
          }
        }
        this.setFields(newFields);
      },

      // 通过键值对设置表单项的初始值initialValue,并调用setFields方法重绘表单页面
      setFieldsInitialValue: function setFieldsInitialValue(initialValues) {
        var fieldsMeta = this.fieldsMeta;
        var virtualPaths = (0, _utils.getVirtualPaths)(fieldsMeta);
        for (var name in initialValues) {
          if (initialValues.hasOwnProperty(name)) {
            if (fieldsMeta[name] && fieldsMeta[name].virtual) {
              for (var i = 0, len = virtualPaths[name].length; i < len; i++) {
                var path = virtualPaths[name][i];
                if ((0, _lodash4["default"])(initialValues, path)) {
                  fieldsMeta[path] = (0, _extends3["default"])({}, fieldsMeta[path], {
                    initialValue: (0, _lodash2["default"])(initialValues, path)
                  });
                }
              }
            } else if (fieldsMeta[name]) {
              fieldsMeta[name] = (0, _extends3["default"])({}, fieldsMeta[name], {
                initialValue: initialValues[name]
              });
            }
          }
        }
      },

      // 通过getCacheBind作为组件实例ref属性,回调函数形式,this.instances添加实例
      saveRef: function saveRef(name, _, component) {
        if (!component) {
          // after destroy, delete data
          delete this.fieldsMeta[name];
          delete this.fields[name];
          delete this.instances[name];
          delete this.cachedBind[name];
          return;
        }
        var fieldMeta = this.getFieldMeta(name);
        if (fieldMeta) {
          var ref = fieldMeta.ref;
          if (ref) {
            if (typeof ref === 'string') {
              throw new Error('can not set ref string for ' + name);
            }
            ref(component);
          }
        }
        this.instances[name] = component;
      },

      // 参数fields为数组形式待校验的表单项
      // _ref2={fieldNames,action,options}校验配置,options又作为async-validate模块的执行配置
      //      options.force强制对表单项作校验,即便field.dirty为false,指示校验完成
      // callback(errors,values)根据fields设置值完成及校验完成后执行的回调函数
      // 调用setFields设置表单项的值并做校验,校验完成后执行setFields重绘校验文案,两次setFields方法后执行callback回调
      validateFieldsInternal: function validateFieldsInternal(fields, _ref2, callback) {
        var _this5 = this;

        var fieldNames = _ref2.fieldNames,
            action = _ref2.action,
            _ref2$options = _ref2.options,
            options = _ref2$options === undefined ? {} : _ref2$options;

        var allRules = {};
        var allValues = {};
        var allFields = {};
        var alreadyErrors = {};
        fields.forEach(function (field) {
          var name = field.name;
          if (options.force !== true && field.dirty === false) {
            if (field.errors) {
              (0, _lodash6["default"])(alreadyErrors, name, { errors: field.errors });
            }
            return;
          }
          var fieldMeta = _this5.getFieldMeta(name);
          var newField = (0, _extends3["default"])({}, field);
          newField.errors = undefined;
          newField.validating = true;
          newField.dirty = true;
          allRules[name] = _this5.getRules(fieldMeta, action);
          allValues[name] = newField.value;
          allFields[name] = newField;
        });
        this.setFields(allFields);
        // in case normalize
        Object.keys(allValues).forEach(function (f) {
          allValues[f] = _this5.getFieldValue(f);
        });
        if (callback && (0, _utils.isEmptyObject)(allFields)) {
          callback((0, _utils.isEmptyObject)(alreadyErrors) ? null : alreadyErrors, this.getFieldsValue((0, _utils.flatFieldNames)(fieldNames)));
          return;
        }
        var validator = new _asyncValidator2["default"](allRules);
        if (validateMessages) {
          validator.messages(validateMessages);
        }
        validator.validate(allValues, options, function (errors) {
          var errorsGroup = (0, _extends3["default"])({}, alreadyErrors);
          if (errors && errors.length) {
            errors.forEach(function (e) {
              var fieldName = e.field;
              if (!(0, _lodash4["default"])(errorsGroup, fieldName)) {
                (0, _lodash6["default"])(errorsGroup, fieldName, { errors: [] });
              }
              var fieldErrors = (0, _lodash2["default"])(errorsGroup, fieldName.concat('.errors'));
              fieldErrors.push(e);
            });
          }
          var expired = [];
          var nowAllFields = {};
          Object.keys(allRules).forEach(function (name) {
            var fieldErrors = (0, _lodash2["default"])(errorsGroup, name);
            var nowField = _this5.getField(name);
            // avoid concurrency problems
            if (nowField.value !== allValues[name]) {
              expired.push({
                name: name
              });
            } else {
              nowField.errors = fieldErrors && fieldErrors.errors;
              nowField.value = allValues[name];
              nowField.validating = false;
              nowField.dirty = false;
              nowAllFields[name] = nowField;
            }
          });
          _this5.setFields(nowAllFields);
          if (callback) {
            if (expired.length) {
              expired.forEach(function (_ref3) {
                var name = _ref3.name;

                var fieldErrors = [{
                  message: name + ' need to revalidate',
                  field: name
                }];
                (0, _lodash6["default"])(errorsGroup, name, {
                  expired: true,
                  errors: fieldErrors
                });
              });
            }

            callback((0, _utils.isEmptyObject)(errorsGroup) ? null : errorsGroup, _this5.getFieldsValue((0, _utils.flatFieldNames)(fieldNames)));
          }
        });
      },

      // 调用validateFieldsInternal方法校验表单,参数ns为各表单项的name,opt校验配置,cb回调函数
      validateFields: function validateFields(ns, opt, cb) {
        var _this6 = this;

        var _getParams = (0, _utils.getParams)(ns, opt, cb),
            names = _getParams.names,
            callback = _getParams.callback,
            options = _getParams.options;

        var fieldNames = names || this.getValidFieldsName();
        var fields = fieldNames.map(function (name) {
          var fieldMeta = _this6.getFieldMeta(name);
          if (!(0, _utils.hasRules)(fieldMeta.validate)) {
            return null;
          }
          var field = _this6.getField(name);
          field.value = _this6.getFieldValue(name);
          return field;
        }).filter(function (f) {
          return !!f;
        });
        if (!fields.length) {
          if (callback) {
            callback(null, this.getFieldsValue((0, _utils.flatFieldNames)(fieldNames)));
          }
          return;
        }
        if (!('firstFields' in options)) {
          options.firstFields = fieldNames.filter(function (name) {
            var fieldMeta = _this6.getFieldMeta(name);
            return !!fieldMeta.validateFirst;
          });
        }
        this.validateFieldsInternal(fields, {
          fieldNames: fieldNames,
          options: options
        }, callback);
      },

      // 判断表单项是否在校验过程中
      isFieldValidating: function isFieldValidating(name) {
        return this.getFieldMember(name, 'validating');
      },

      // 判断表单中是否有某个表单项在校验过程中
      isFieldsValidating: function isFieldsValidating(ns) {
        var _this7 = this;

        var names = ns || this.getValidFieldsName();
        return names.some(function (n) {
          return _this7.isFieldValidating(n);
        });
      },

      // 判断表单的提交状态submitting
      isSubmitting: function isSubmitting() {
        return this.state.submitting;
      },

      // 设置表单的提交状态submitting为真值,提交完成后执行callback回调,提交状态submitting改为否值
      submit: function submit(callback) {
        var _this8 = this;

        var fn = function fn() {
          _this8.setState({
            submitting: false
          });
        };
        this.setState({
          submitting: true
        });
        callback(fn);
      },

      // 重置表单,各表单项的值、校验文案均清空
      resetFields: function resetFields(ns) {
        var newFields = {};
        var fields = this.fields;

        var changed = false;
        var names = ns || Object.keys(fields);
        names.forEach(function (name) {
          var field = fields[name];
          if (field && 'value' in field) {
            changed = true;
            newFields[name] = {};
          }
        });
        if (changed) {
          this.setFields(newFields);
        }
      },
      render: function render() {
        // 向下游WrappedComponent组件添加this.props.form工具函数集
        var formProps = (0, _defineProperty3["default"])({}, formPropName, this.getForm());
        // 向下游WrappedComponent组件添加ref属性
        if (withRef) {
          formProps.ref = 'wrappedComponent';
        }
        var props = mapProps.call(this, (0, _extends3["default"])({}, formProps, this.props));
        return _react2["default"].createElement(WrappedComponent, props);
      }
    });

    // 将WrappedComponent的非react静态方法拷贝给Form,何种意义???
    return (0, _utils.argumentContainer)(Form, WrappedComponent);
  }

  return decorate;
}

exports["default"] = createBaseForm;
module.exports = exports['default'];

2.createForm.js

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.mixin = undefined;

var _createBaseForm = require('./createBaseForm');
var _createBaseForm2 = _interopRequireDefault(_createBaseForm);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

var mixin = exports.mixin = {
  getForm: function getForm() {
    return {
      getFieldsValue: this.getFieldsValue,
      getFieldValue: this.getFieldValue,
      getFieldInstance: this.getFieldInstance,
      setFieldsValue: this.setFieldsValue,
      setFields: this.setFields,
      setFieldsInitialValue: this.setFieldsInitialValue,
      getFieldDecorator: this.getFieldDecorator,
      getFieldProps: this.getFieldProps,
      getFieldError: this.getFieldError,
      isFieldValidating: this.isFieldValidating,
      isFieldsValidating: this.isFieldsValidating,
      isSubmitting: this.isSubmitting,
      submit: this.submit,
      validateFields: this.validateFields,
      resetFields: this.resetFields
    };
  }
};

function createForm(options) {
  return (0, _createBaseForm2["default"])(options, [mixin]);
}

exports["default"] = createForm;

createDomForm.js

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);

var _createBaseForm = require('./createBaseForm');
var _createBaseForm2 = _interopRequireDefault(_createBaseForm);

var _createForm = require('./createForm');

var _utils = require('./utils');

var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);

var _domScrollIntoView = require('dom-scroll-into-view');
var _domScrollIntoView2 = _interopRequireDefault(_domScrollIntoView);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

// 获取样式
function computedStyle(el, prop) {
  var getComputedStyle = window.getComputedStyle;
  var style =
  // If we have getComputedStyle
  getComputedStyle ?
  // Query it
  // TODO: From CSS-Query notes, we might need (node, null) for FF
  getComputedStyle(el) :

  // Otherwise, we are in IE and use currentStyle
  el.currentStyle;
  if (style) {
    return style[
    // Switch to camelCase for CSSOM
    // DEV: Grabbed from jQuery
    // https://github.com/jquery/jquery/blob/1.9-stable/src/css.js#L191-L194
    // https://github.com/jquery/jquery/blob/1.9-stable/src/core.js#L593-L597
    prop.replace(/-(\w)/gi, function (word, letter) {
      return letter.toUpperCase();
    })];
  }
  return undefined;
}

// 获取设置overflow样式的容器节点
function getScrollableContainer(n) {
  var node = n;
  var nodeName = void 0;
  /* eslint no-cond-assign:0 */
  while ((nodeName = node.nodeName.toLowerCase()) !== 'body') {
    var overflowY = computedStyle(node, 'overflowY');
    if (overflowY === 'auto' || overflowY === 'scroll') {
      return node;
    }
    node = node.parentNode;
  }
  return nodeName === 'body' ? node.ownerDocument : node;
}

var mixin = {
  // createBaseForm模块中往下游组件传递this.props.form工具函数集
  // 提供getFieldProps、getFieldDecorator、validateFields、validateFieldsAndScroll、setFields等方法
  getForm: function getForm() {
    return (0, _extends3["default"])({}, _createForm.mixin.getForm.call(this), {
      validateFieldsAndScroll: this.validateFieldsAndScroll
    });
  },
  validateFieldsAndScroll: function validateFieldsAndScroll(ns, opt, cb) {
    var _this = this;

    var _getParams = (0, _utils.getParams)(ns, opt, cb),
        names = _getParams.names,
        callback = _getParams.callback,
        options = _getParams.options;

    var newCb = function newCb(error, values) {
      if (error) {
        var firstNode = void 0;
        var firstTop = void 0;
        for (var name in error) {
          if (error.hasOwnProperty(name)) {
            var instance = _this.getFieldInstance(name);
            if (instance) {
              var node = _reactDom2["default"].findDOMNode(instance);
              var top = node.getBoundingClientRect().top;
              if (firstTop === undefined || firstTop > top) {
                firstTop = top;
                firstNode = node;
              }
            }
          }
        }
        if (firstNode) {
          var c = options.container || getScrollableContainer(firstNode);
          (0, _domScrollIntoView2["default"])(firstNode, c, (0, _extends3["default"])({
            onlyScrollIfNeeded: true
          }, options.scroll));
        }
      }

      if (typeof callback === 'function') {
        callback(error, values);
      }
    };

    return this.validateFields(names, options, newCb);
  }
};

function createDOMForm(option) {
  return (0, _createBaseForm2["default"])((0, _extends3["default"])({}, option), [mixin]);
}

exports["default"] = createDOMForm;
module.exports = exports['default'];

utils.js

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);

var _createBaseForm = require('./createBaseForm');
var _createBaseForm2 = _interopRequireDefault(_createBaseForm);

var _createForm = require('./createForm');

var _utils = require('./utils');

var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);

var _domScrollIntoView = require('dom-scroll-into-view');
var _domScrollIntoView2 = _interopRequireDefault(_domScrollIntoView);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

// 获取样式
function computedStyle(el, prop) {
  var getComputedStyle = window.getComputedStyle;
  var style =
  // If we have getComputedStyle
  getComputedStyle ?
  // Query it
  // TODO: From CSS-Query notes, we might need (node, null) for FF
  getComputedStyle(el) :

  // Otherwise, we are in IE and use currentStyle
  el.currentStyle;
  if (style) {
    return style[
    // Switch to camelCase for CSSOM
    // DEV: Grabbed from jQuery
    // https://github.com/jquery/jquery/blob/1.9-stable/src/css.js#L191-L194
    // https://github.com/jquery/jquery/blob/1.9-stable/src/core.js#L593-L597
    prop.replace(/-(\w)/gi, function (word, letter) {
      return letter.toUpperCase();
    })];
  }
  return undefined;
}

// 获取设置overflow样式的容器节点
function getScrollableContainer(n) {
  var node = n;
  var nodeName = void 0;
  /* eslint no-cond-assign:0 */
  while ((nodeName = node.nodeName.toLowerCase()) !== 'body') {
    var overflowY = computedStyle(node, 'overflowY');
    if (overflowY === 'auto' || overflowY === 'scroll') {
      return node;
    }
    node = node.parentNode;
  }
  return nodeName === 'body' ? node.ownerDocument : node;
}

var mixin = {
  // createBaseForm模块中往下游组件传递this.props.form工具函数集
  // 提供getFieldProps、getFieldDecorator、validateFields、validateFieldsAndScroll、setFields等方法
  getForm: function getForm() {
    return (0, _extends3["default"])({}, _createForm.mixin.getForm.call(this), {
      validateFieldsAndScroll: this.validateFieldsAndScroll
    });
  },
  validateFieldsAndScroll: function validateFieldsAndScroll(ns, opt, cb) {
    var _this = this;

    var _getParams = (0, _utils.getParams)(ns, opt, cb),
        names = _getParams.names,
        callback = _getParams.callback,
        options = _getParams.options;

    var newCb = function newCb(error, values) {
      if (error) {
        var firstNode = void 0;
        var firstTop = void 0;
        for (var name in error) {
          if (error.hasOwnProperty(name)) {
            var instance = _this.getFieldInstance(name);
            if (instance) {
              var node = _reactDom2["default"].findDOMNode(instance);
              var top = node.getBoundingClientRect().top;
              if (firstTop === undefined || firstTop > top) {
                firstTop = top;
                firstNode = node;
              }
            }
          }
        }
        if (firstNode) {
          var c = options.container || getScrollableContainer(firstNode);
          (0, _domScrollIntoView2["default"])(firstNode, c, (0, _extends3["default"])({
            onlyScrollIfNeeded: true
          }, options.scroll));
        }
      }

      if (typeof callback === 'function') {
        callback(error, values);
      }
    };

    return this.validateFields(names, options, newCb);
  }
};

function createDOMForm(option) {
  return (0, _createBaseForm2["default"])((0, _extends3["default"])({}, option), [mixin]);
}

exports["default"] = createDOMForm;
module.exports = exports['default'];

index.js

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createForm = undefined;

var _createForm = require('./createForm');
var _createForm2 = _interopRequireDefault(_createForm);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

exports.createForm = _createForm2["default"]; // export this package's api

3.hoist-non-react-statics模块用于拷贝某组件的非react静态方法

/**
 * Copyright 2015, Yahoo! Inc.
 * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */
'use strict';

var REACT_STATICS = {
    childContextTypes: true,
    contextTypes: true,
    defaultProps: true,
    displayName: true,
    getDefaultProps: true,
    mixins: true,
    propTypes: true,
    type: true
};

var KNOWN_STATICS = {
    name: true,
    length: true,
    prototype: true,
    caller: true,
    arguments: true,
    arity: true
};

var isGetOwnPropertySymbolsAvailable = typeof Object.getOwnPropertySymbols === 'function';

// 将sourceComponent的非react静态方法拷贝给targetComponent,REACT_STATICS、KNOWN_STATICS、customStatics中的除外
module.exports = function hoistNonReactStatics(targetComponent, sourceComponent, customStatics) {
    if (typeof sourceComponent !== 'string') { // don't hoist over string (html) components
        var keys = Object.getOwnPropertyNames(sourceComponent);

        /* istanbul ignore else */
        if (isGetOwnPropertySymbolsAvailable) {
            keys = keys.concat(Object.getOwnPropertySymbols(sourceComponent));
        }

        for (var i = 0; i < keys.length; ++i) {
            if (!REACT_STATICS[keys[i]] && !KNOWN_STATICS[keys[i]] && (!customStatics || !customStatics[keys[i]])) {
                try {
                    targetComponent[keys[i]] = sourceComponent[keys[i]];
                } catch (error) {

                }
            }
        }
    }

    return targetComponent;
};

猜你喜欢

转载自schifred.iteye.com/blog/2354776