ReactElementValidator

ReactElementValidator模块,对外接口,内部调用ReactElement,用于创建、克隆ReactElement,或创建ReactElement的工厂函数,将校验组件的构造函数、使用propsType校验props、校验props.children是否ReactElement及多个子元素时包含key。

'use strict';

// 保存容器组件,即用户自定义组件的ReactCompositeComponent实例,添加ref引用的需要
// 同时用于提示用户加载的组件模块书写错误
var ReactCurrentOwner = require('./ReactCurrentOwner');

// ReactComponentTreeHook.getCurrentStackAddendum用于获取容器组价的信息
var ReactComponentTreeHook = require('./ReactComponentTreeHook');

var ReactElement = require('./ReactElement');

// 根据组件的propTypes静态属性校验props
var checkReactTypeSpec = require('./checkReactTypeSpec');

// 可否使用Object.defineProperty方法  
var canDefineProperty = require('./canDefineProperty');

var getIteratorFn = require('./getIteratorFn');

// warning(condition,format) condition为否值,替换format中的"%s",并console.error警告
var warning = require('fbjs/lib/warning');

// 提示用户书写有错的组件构造函数名
function getDeclarationErrorAddendum() {
  if (ReactCurrentOwner.current) {
    var name = ReactCurrentOwner.current.getName();
    if (name) {
      return ' Check the render method of `' + name + '`.';
    }
  }
  return '';
}

// 初次创建时警告,props.children未改变引起的更新不警告
var ownerHasKeyUseWarning = {};

// props.children的直系父组件信息
function getCurrentComponentErrorInfo(parentType) {
  var info = getDeclarationErrorAddendum();

  if (!info) {
    var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name;
    if (parentName) {
      info = ' Check the top-level render call using <' + parentName + '>.';
    }
  }
  return info;
}

// props.children单个值中含有多个ReactElement时,缺失key值警告直系父组件书写有误
function validateExplicitKey(element, parentType) {
  if (!element._store || element._store.validated || element.key != null) {
    return;
  }
  element._store.validated = true;

  var memoizer = ownerHasKeyUseWarning.uniqueKey || (ownerHasKeyUseWarning.uniqueKey = {});

  // 获取props.children的直系父组件信息
  var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
  if (memoizer[currentComponentErrorInfo]) {
    return;
  }
  memoizer[currentComponentErrorInfo] = true;

  var childOwner = '';
  if (element && element._owner && element._owner !== ReactCurrentOwner.current) {
    childOwner = ' It was passed a child from ' + element._owner.getName() + '.';
  }

  process.env.NODE_ENV !== 'production' ? 
    warning(false, 'Each child in an array or iterator should have a unique "key" prop.' 
      + '%s%s See https://fb.me/react-warning-keys for more information.%s', 
      currentComponentErrorInfo, childOwner, ReactComponentTreeHook.getCurrentStackAddendum(element)) 
    : void 0;
}

// 校验props.children的每项,单个值只校验是否ReactElement,多个值再校验是否存在key属性
function validateChildKeys(node, parentType) {
  if (typeof node !== 'object') {
    return;
  }

  // 数组形式
  if (Array.isArray(node)) {
    for (var i = 0; i < node.length; i++) {
      var child = node[i];
      if (ReactElement.isValidElement(child)) {
        validateExplicitKey(child, parentType);
      }
    }

  // 单个值
  } else if (ReactElement.isValidElement(node)) {
    if (node._store) {
      node._store.validated = true;
    }

  // 迭代器形式???
  } else if (node) {
    var iteratorFn = getIteratorFn(node);
    if (iteratorFn) {
      if (iteratorFn !== node.entries) {
        var iterator = iteratorFn.call(node);
        var step;
        while (!(step = iterator.next()).done) {
          if (ReactElement.isValidElement(step.value)) {
            validateExplicitKey(step.value, parentType);
          }
        }
      }
    }
  }
}

// 根据组件的propTypes静态属性校验props,同时提示getDefaultProps只能用于React.createClass方法创建的组件
function validatePropTypes(element) {
  var componentClass = element.type;
  if (typeof componentClass !== 'function') {
    return;
  }
  var name = componentClass.displayName || componentClass.name;
  if (componentClass.propTypes) {
    checkReactTypeSpec(componentClass.propTypes, element.props, 'prop', name, element, null);
  }
  if (typeof componentClass.getDefaultProps === 'function') {
    process.env.NODE_ENV !== 'production' ? 
      warning(componentClass.getDefaultProps.isReactClassApproved, 
        'getDefaultProps is only used on classic React.createClass ' 
        + 'definitions. Use a static property named `defaultProps` instead.') 
      : void 0;
  }
}

var ReactElementValidator = {

  createElement: function (type, props, children) {
    var validType = typeof type === 'string' || typeof type === 'function';

    // 传参type用户自定义组件构造函数、或ReactDomComponent的类型字符串有误
    if (!validType) {
      if (typeof type !== 'function' && typeof type !== 'string') {
        var info = '';

        // 用于提示用户加载的组件模块可能尚未导出组件的构造函数
        if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {
          info += ' You likely forgot to export your component from the file ' + 'it\'s defined in.';
        }

        // 提示用户书写有错的组件构造函数名
        info += getDeclarationErrorAddendum();
        process.env.NODE_ENV !== 'production' ? 
          warning(false, 'React.createElement: type is invalid -- expected a string (for ' 
            + 'built-in components) or a class/function (for composite ' 
            + 'components) but got: %s.%s', type == null ? type : typeof type, info) 
          : void 0;
      }
    }

    var element = ReactElement.createElement.apply(this, arguments);

    if (element == null) {
      return element;
    }

    // 校验props.children的每项,单个值只校验是否ReactElement,多个值再校验是否存在key属性
    if (validType) {
      for (var i = 2; i < arguments.length; i++) {
        validateChildKeys(arguments[i], type);
      }
    }

    // 根据组件的propTypes静态属性校验props,同时提示getDefaultProps只能用于React.createClass方法创建的组件
    validatePropTypes(element);

    return element;
  },

  createFactory: function (type) {
    var validatedFactory = ReactElementValidator.createElement.bind(null, type);
    validatedFactory.type = type;

    // 提示自定义组件的构造函数、及ReactDomComponent的类型字符串不能获取
    if (process.env.NODE_ENV !== 'production') {
      if (canDefineProperty) {
        Object.defineProperty(validatedFactory, 'type', {
          enumerable: false,
          get: function () {
            process.env.NODE_ENV !== 'production' ? 
              warning(false, 'Factory.type is deprecated. Access the class directly ' 
                + 'before passing it to createFactory.') 
              : void 0;
            Object.defineProperty(this, 'type', {
              value: type
            });
            return type;
          }
        });
      }
    }

    return validatedFactory;
  },

  cloneElement: function (element, props, children) {
    var newElement = ReactElement.cloneElement.apply(this, arguments);
    for (var i = 2; i < arguments.length; i++) {
      validateChildKeys(arguments[i], newElement.type);
    }
    validatePropTypes(newElement);
    return newElement;
  }

};

module.exports = ReactElementValidator;

相关推荐