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

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

## ReactDOM.js

[URL](https://github.com/facebook/react/blob/master/src/renderers/dom/ReactDOM.js)

```js: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

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

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

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

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

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

```js: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の作成を見に行く。

```js: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を返している。

```js: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の種類を変えている。
一つずつ見てみる。

```js:ReactEmptyComponent.js
var ReactEmptyComponent = {
  create: function(instantiate) {
    return emptyComponentFactory(instantiate);
  },
};
```
nextElementが空の場合。
FactoryがinstantiateReactComponentを引数にして何かを実行するfunctionを返す。

```js: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であればそれを適用するようだ。

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

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

```js: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している処理を見に行く。

```js: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をまずは解明しないと無理そうだ。。