抹桥的博客
Language
Home
Archive
About
GitHub
Language
主题色
250
1227 文字
6 分
React ソースコード入門 - ReactElement

ReactElement#

このモジュールはReact要素の振る舞いとメソッドを定義しています。まず、ReactElement関数を見てみましょう。

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;
};

DEV内の内容を除けば、他の部分は非常にシンプルで、React要素が持つべき属性を定義しています。これには、type, key, ref, self, source, owner, propsが含まれます。

また、$$typeof属性という定数があり、これはそのオブジェクトがReact要素であるかどうかを判断するために使用されます。

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,
  );
};

与えられた引数を使ってReact要素を作成します。refkeyは予約済みフィールドであり、propsの属性として渡すことはできない点に注意が必要です。

ReactElement.createFactory#

同じ型のコンポーネントを簡単に作成するためのシンプルなファクトリ関数です。

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#

このAPIは使用したことがありませんが、React要素の予約済み属性であるkey値を置き換えるために使用できます。

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#

cloneElementメソッドはcreateElementと基本的に同じですが、前者は既存のReact要素から新しい要素を複製するという点が異なります。

ReactElement.isValidElement#

オブジェクトが有効なReact要素であるかどうかを判断するために使用されます。

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

上で最初に定義した$$typeof属性が使われています。

export#

module.exports = ReactElement;

ReactElementモジュールはこれらで構成されており、主にReact要素が持つべき属性と、要素を操作するためのいくつかのメソッドを定義しています。

関連記事#

  • {% post_link react-source-code-analyze-1 Reactソースコードの深掘り - エントリファイル編 %}
  • {% post_link react-source-code-analyze-2 Reactソースコードの深掘り - ReactBaseClasses編 %}
  • {% post_link react-source-code-analyze-3 Reactソースコードの深掘り - ReactChildren編 %}
  • {% post_link react-source-code-analyze-4 Reactソースコードの深掘り - ReactElement編 %}
  • {% post_link react-source-code-analyze-5 Reactソースコードの深掘り - onlyChildren編 %}

この記事は 2017年9月21日 に公開され、2017年9月21日 に最終更新されました。2937 日が経過しており、内容が古くなっている可能性があります。

React ソースコード入門 - ReactElement
https://blog.kisnows.com/ja-JP/2017/09/21/react-source-code-analyze-4/
作者
Kisnows
公開日
2017-09-21
ライセンス
CC BY-NC-ND 4.0