7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Reactの内部を見てみる~ReactDOM編~

Last updated at Posted at 2016-03-12

Reactの内部を見てみる~ReactDOM編~

ReactDOM.js

URL

ReactDOM.js
var React = {
  findDOMNode: findDOMNode,
  render: render,
  unmountComponentAtNode: ReactMount.unmountComponentAtNode,
  version: ReactVersion,

  /* eslint-disable camelcase */
  unstable_batchedUpdates: ReactUpdates.batchedUpdates,
  unstable_renderSubtreeIntoContainer: renderSubtreeIntoContainer,
  /* eslint-enable camelcase */
};

本体はここのようだ。
Tutorialでお世話になったrenderを追っていく。

ReactDOM.render

ReactDOM.render
var ReactMount = require('ReactMount');
var ReactPerf = require('ReactPerf');

var render = ReactPerf.measure('React', 'render', ReactMount.render);

ReactPerf.measureの返り値なのでReactPerfを見てみる。

ReactPerf.js
var ReactPerf = {
  ...,
  
  measure: function(objName, fnName, func) {
    ...,
    
    return func;
  }
};

productionではfuncをそのまま返しているだけなので、実態はReactMountにあるようだ。

ReactMount.js
var ReactMount = {
  ...,

  _renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
    ...,

    var component = ReactMount._renderNewRootComponent(
      nextWrappedElement,
      container,
      shouldReuseMarkup,
      parentComponent != null ?
        parentComponent._reactInternalInstance._processChildContext(
          parentComponent._reactInternalInstance._context
        ) :
        emptyObject
    )._renderedComponent.getPublicInstance();
    if (callback) {
      callback.call(component);
    }
    return component;
  },

  ...,

  render: function(nextElement, container, callback) {
      return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
  }
}

containerは描画するelementの外側のDOMとなる。
最終的には新しいcomponentを返すだけ。しかし、それまでにかなりのエラー判定などがある。。(ここでは掘り下げないようにしよう・・)
componentの作成を見に行く。

ReactMount.js(component作成)
  ...,

  _renderNewRootComponent: function(
    nextElement,
    container,
    shouldReuseMarkup,
    context
  ) {
    ...,

    var componentInstance = instantiateReactComponent(nextElement);

    ReactUpdates.batchedUpdates(
      batchedMountComponentIntoNode,
      componentInstance,
      container,
      shouldReuseMarkup,
      context
    );

    ...,

    return componentInstance;
  },

componentInstanceを作成して、何かのupdateをしてcomponentInstanceを返している。

instantiateReactComponent.js
function instantiateReactComponent(node) {
  var instance;

  if (node === null || node === false) {
    instance = ReactEmptyComponent.create(instantiateReactComponent);
  } else if (typeof node === 'object') {
    var element = node;

    // Special case string values
    if (typeof element.type === 'string') {
      instance = ReactNativeComponent.createInternalComponent(element);
    } else if (isInternalComponentType(element.type)) {
      // This is temporarily available for custom components that are not string
      // representations. I.e. ART. Once those are updated to use the string
      // representation, we can drop this code path.
      instance = new element.type(element);
    } else {
      instance = new ReactCompositeComponentWrapper(element);
    }
  } else if (typeof node === 'string' || typeof node === 'number') {
    instance = ReactNativeComponent.createInstanceForText(node);
  } else {
    // エラー処理
  }

  ...,

  return instance;
}

nextElementが空だったら空のcomponentを返しているようだ。
あとはnodeのtypeによって作成するcomponentの種類を変えている。
一つずつ見てみる。

ReactEmptyComponent.js
var ReactEmptyComponent = {
  create: function(instantiate) {
    return emptyComponentFactory(instantiate);
  },
};

nextElementが空の場合。
FactoryがinstantiateReactComponentを引数にして何かを実行するfunctionを返す。

ReactNativeComponent.js
var genericComponentClass = null;
var textComponentClass = null;

function createInternalComponent(element) {
  invariant(
    genericComponentClass,
    'There is no registered component for the tag %s',
    element.type
  );
  return new genericComponentClass(element);
}

function createInstanceForText(text) {
  return new textComponentClass(text);
}

var ReactNativeComponent = {
  getComponentClassForElement: getComponentClassForElement,
  createInternalComponent: createInternalComponent,
  createInstanceForText: createInstanceForText,
  isTextComponent: isTextComponent,
  injection: ReactNativeComponentInjection,
};

nextElementが{ type: ..., }と{...}とstring,numberの場合。
この時点ではどちらもnullなのでどういうことか不明。とは言ってもelementはJSXで表現されているので、JSXで何かをしている可能性がある。
なお、typeがReactで定義されているfunctionであればそれを適用するようだ。

ReactCompositeComponentWrapper
var ReactCompositeComponentWrapper = function(element) {
  this.construct(element);
};
assign(
  ReactCompositeComponentWrapper.prototype,
  ReactCompositeComponent.Mixin,
  {
    _instantiateReactComponent: instantiateReactComponent,
  }
);

ReactCompositeComponent.Mixinに実態がある。見てみる。

ReactCompositeComponent.js
var ReactCompositeComponentMixin = {

  construct: function(element) {
    this._currentElement = element;
    this._rootNodeID = null;
    this._instance = null;
    this._nativeParent = null;
    this._nativeContainerInfo = null;

    // See ReactUpdateQueue
    this._pendingElement = null;
    this._pendingStateQueue = null;
    this._pendingReplaceState = false;
    this._pendingForceUpdate = false;

    this._renderedNodeType = null;
    this._renderedComponent = null;
    this._context = null;
    this._mountOrder = 0;
    this._topLevelWrapper = null;

    // See ReactUpdates and ReactUpdateQueue.
    this._pendingCallbacks = null;
  },

  ...,
};

var ReactCompositeComponent = {

  Mixin: ReactCompositeComponentMixin,

};

currentElementにnextElementを持つobjectを作成するようだ。
次にupdateしている処理を見に行く。

ReactUpdates.js
function batchedUpdates(callback, a, b, c, d, e) {
  ensureInjected();
  batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}

var ReactUpdates = {
  /**
   * React references `ReactReconcileTransaction` using this property in order
   * to allow dependency injection.
   *
   * @internal
   */
  ReactReconcileTransaction: null,

  batchedUpdates: batchedUpdates,
  enqueueUpdate: enqueueUpdate,
  flushBatchedUpdates: flushBatchedUpdates,
  injection: ReactUpdatesInjection,
  asap: asap,
};

この時点ではensureInjected()でエラーが出る。どういうことだ。。injectするところがあるか探す。
おそらくnextElementがJSXであるところにヒントがあるように思える。
JSXをまずは解明しないと無理そうだ。。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?