抹桥的博客
Language
Home
Archive
About
GitHub
Language
主题色
250
1001 words
5 minutes
React Source Code Explained - ReactElement

ReactElement#

This module defines the behavior and methods of React elements. Let’s start by looking at the ReactElement function:

ReactElement#

var ReactElement = function (type, key, ref, self, source, owner, props) {
  var element = {
    // This tag allow us to uniquely identify this as a React Element
    $typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner,
  };

  if (__DEV__) {
    // The validation flag is currently mutative. We put it on
    // an external backing store so that we can freeze the whole object.
    // This can be replaced with a WeakMap once they are implemented in
    // commonly used development environments.
    element._store = {};

    // To make comparing ReactElements easier for testing purposes, we make
    // the validation flag non-enumerable (where possible, which should
    // include every environment we run tests in), so the test framework
    // ignores it.
    Object.defineProperty(element._store, "validated", {
      configurable: false,
      enumerable: false,
      writable: true,
      value: false,
    });
    // self and source are DEV only properties.
    Object.defineProperty(element, "_self", {
      configurable: false,
      enumerable: false,
      writable: false,
      value: self,
    });
    // Two elements created in two different places should be considered
    // equal for testing purposes and therefore we hide it from enumeration.
    Object.defineProperty(element, "_source", {
      configurable: false,
      enumerable: false,
      writable: false,
      value: source,
    });
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element);
    }
  }

  return element;
};

Excluding the content within DEV, the rest is straightforward, defining the properties a React element should have. These include: type, key, ref, self, source, owner, props.

There’s also a $$typeof property, which is a constant used to determine if an object is a React element.

var REACT_ELEMENT_TYPE =
  (typeof Symbol === "function" && Symbol.for && Symbol.for("react.element")) ||
  0xeac7;

ReactElement.createElement#

ReactElement.createElement = function (type, config, children) {
  var propName;

  // Reserved names are extracted
  var props = {};

  var key = null;
  var ref = null;
  var self = null;
  var source = null;

  if (config != null) {
    // ref 和 key 都属于保留 props key 值,所以这里需要做判断
    if (hasValidRef(config)) {
      ref = config.ref;
    }
    if (hasValidKey(config)) {
      key = "" + config.key;
    }
    // __self 和 __source 这两个属性目前没有看到他们的作用,先放着
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // Remaining properties are added to a new props object
    // 其他的属性添加到新的 props 对象上,同时需要排除掉保留字段 RESERVED_PROPS
    // var RESERVED_PROPS = {key: true, ref: true, __self: true, __source: true,};
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
  }

  // Children 可以传递一个以上的参数,这些 children 参数都会作为新分配的 props 的属性
  var childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    var childArray = Array(childrenLength);
    for (var i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    if (__DEV__) {
      if (Object.freeze) {
        Object.freeze(childArray);
      }
    }
    props.children = childArray;
  }

  // 设置 defaultProps 属性
  if (type && type.defaultProps) {
    var defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  // 开发环境下,如果使用了保留字段 key 和 ref ,那么进行控制台报错提醒
  if (__DEV__) {
    if (key || ref) {
      if (
        typeof props.$typeof === "undefined" ||
        props.$typeof !== REACT_ELEMENT_TYPE
      ) {
        var displayName =
          typeof type === "function"
            ? type.displayName || type.name || "Unknown"
            : type;
        if (key) {
          defineKeyPropWarningGetter(props, displayName);
        }
        if (ref) {
          defineRefPropWarningGetter(props, displayName);
        }
      }
    }
  }
  // 返回一个 React 元素, ReactCurrentOwner.current 是指当前正处于构建过程中的组件,这里默认是 null
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
};

This creates a React element using the given parameters. It’s important to note that ref and key are reserved fields and cannot be passed as properties within props.

ReactElement.createFactory#

A simple factory function used to conveniently create components of the same type.

ReactElement.createFactory = function (type) {
  var factory = ReactElement.createElement.bind(null, type);
  // Expose the type on the factory and the prototype so that it can be
  // easily accessed on elements. E.g. `<Foo />.type === Foo`.
  // This should not be named `constructor` since this may not be the function
  // that created the element, and it may not even be a constructor.
  // Legacy hook TODO: Warn if this is accessed
  factory.type = type;
  return factory;
};

ReactElement.cloneAndReplaceKey#

I haven’t used this API before, but it can be used to replace a reserved property of a React element: the key value.

ReactElement.cloneAndReplaceKey = function (oldElement, newKey) {
  var newElement = ReactElement(
    oldElement.type,
    newKey,
    oldElement.ref,
    oldElement._self,
    oldElement._source,
    oldElement._owner,
    oldElement.props,
  );

  return newElement;
};

ReactElement.cloneElement#

The cloneElement method is essentially the same as createElement, except that the former creates a new element by copying an existing React element.

ReactElement.isValidElement#

Used to determine if an object is a valid React element.

ReactElement.isValidElement = function (object) {
  return (
    typeof object === "object" &&
    object !== null &&
    object.$typeof === REACT_ELEMENT_TYPE
  );
};

It uses the $$typeof property defined at the beginning.

export#

module.exports = ReactElement;

That’s all for the ReactElement module. It primarily defines the properties a React element should have, along with some methods for manipulating elements.

Related Articles#

  • {% post_link react-source-code-analyze-1 React Source Code Analysis - Entry File %}
  • {% post_link react-source-code-analyze-2 React Source Code Analysis - ReactBaseClasses %}
  • {% post_link react-source-code-analyze-3 React Source Code Analysis - ReactChildren %}
  • {% post_link react-source-code-analyze-4 React Source Code Analysis - ReactElement %}
  • {% post_link react-source-code-analyze-5 React Source Code Analysis - onlyChildren %}

This article was published on September 21, 2017 and last updated on September 21, 2017, 2937 days ago. The content may be outdated.

React Source Code Explained - ReactElement
https://blog.kisnows.com/en-US/2017/09/21/react-source-code-analyze-4/
Author
Kisnows
Published at
2017-09-21
License
CC BY-NC-ND 4.0