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


## JSX

* [公式サイト](https://facebook.github.io/react/docs/jsx-in-depth.html)
* JSXの`<Type />`はReact.createElement(Type, attribute, children)に変換されるだけだった

## ReactDOM続き

JSXは特にinjectをしていないようだ。。さて、困った。
さて、ReactDOMでももう一度見てみるか・・

```js:ReactDOM.js
ReactDefaultInjection.inject();

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

・・・先にinjectが実行されていた！よかった（涙）

```js:ReactDefaultInjection.js
function inject() {

  ...,

  ReactInjection.Updates.injectBatchingStrategy(
    ReactDefaultBatchingStrategy
  );
} 
```

ReactInjectionを見に行く。

```js:ReactInjection.js
var ReactInjection = {
  Component: ReactComponentEnvironment.injection,
  Class: ReactClass.injection,
  DOMProperty: DOMProperty.injection,
  EmptyComponent: ReactEmptyComponent.injection,
  EventPluginHub: EventPluginHub.injection,
  EventPluginUtils: EventPluginUtils.injection,
  EventEmitter: ReactBrowserEventEmitter.injection,
  NativeComponent: ReactNativeComponent.injection,
  Perf: ReactPerf.injection,
  Updates: ReactUpdates.injection,
};
```

種別ごとにinjectのmethodを持つだけ。ReactUpdatesのinjectionを見てみる。

```js:ReactUpdates.js
var ReactUpdatesInjection = {
  injectReconcileTransaction: function(ReconcileTransaction) {
    invariant(
      ReconcileTransaction,
      'ReactUpdates: must provide a reconcile transaction class'
    );
    ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;
  },

  injectBatchingStrategy: function(_batchingStrategy) {
    invariant(
      _batchingStrategy,
      'ReactUpdates: must provide a batching strategy'
    );
    invariant(
      typeof _batchingStrategy.batchedUpdates === 'function',
      'ReactUpdates: must provide a batchedUpdates() function'
    );
    invariant(
      typeof _batchingStrategy.isBatchingUpdates === 'boolean',
      'ReactUpdates: must provide an isBatchingUpdates boolean attribute'
    );
    batchingStrategy = _batchingStrategy;
  },
};
```

ようやく想定通りの処理にたどり着けた。よかった。
_batchingStrategyである、ReactDefaultBatchingStrategyを見に行く。

```js:ReactDefaultBatchingStrategy.js
var ReactDefaultBatchingStrategy = {
  isBatchingUpdates: false,

  /**
   * Call the provided function in a context within which calls to `setState`
   * and friends are batched such that components aren't updated unnecessarily.
   */
  batchedUpdates: function(callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    // The code is written this way to avoid extra allocations
    if (alreadyBatchingUpdates) {
      callback(a, b, c, d, e);
    } else {
      transaction.perform(callback, null, a, b, c, d, e);
    }
  },
};
```

初回は普通にcallbackが呼ばれているだけだった。
さて、そのcallbackとなるbatchedMountComponentIntoNodeを見に行く。

```js:ReactMount.js
function batchedMountComponentIntoNode(
  componentInstance,
  container,
  shouldReuseMarkup,
  context
) {
  var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
    /* useCreateElement */
    !shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement
  );
  transaction.perform(
    mountComponentIntoNode,
    null,
    componentInstance,
    container,
    transaction,
    shouldReuseMarkup,
    context
  );
  ReactUpdates.ReactReconcileTransaction.release(transaction);
}
```

さて、この時点だとReactReconcileTransactionがnull・・・
どこかでと思ったら、ちゃんとinjectされていた。

```js:ReactDefaultInjection.js
  ...,

  ReactInjection.Updates.injectReconcileTransaction(
    ReactReconcileTransaction
  );

  ...,
```

```js:ReactUpdates.js
var ReactUpdatesInjection = {
  injectReconcileTransaction: function(ReconcileTransaction) {
    invariant(
      ReconcileTransaction,
      'ReactUpdates: must provide a reconcile transaction class'
    );
    ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;
  },
  ...,
}
```

ReactUpdates.ReactReconcileTransactionとなるReconcileTransactionを見てみる。

```js:ReactReconcileTransaction.js
function ReactReconcileTransaction(useCreateElement) {
  this.reinitializeTransaction();
  // Only server-side rendering really needs this option (see
  // `ReactServerRendering`), but server-side uses
  // `ReactServerRenderingTransaction` instead. This option is here so that it's
  // accessible and defaults to false when `ReactDOMComponent` and
  // `ReactTextComponent` checks it in `mountComponent`.`
  this.renderToStaticMarkup = false;
  this.reactMountReady = CallbackQueue.getPooled(null);
  this.useCreateElement = useCreateElement;
}

assign(ReactReconcileTransaction.prototype, Transaction.Mixin, Mixin);

PooledClass.addPoolingTo(ReactReconcileTransaction);
```

MixinにgetPooledがないので、Transaction.Mixinを見に行く。

```js:Transaction
var Mixin = {
  ...,
}
```

それっぽいのがないので、PooledClassを見に行く。

```js:PooledClass
var addPoolingTo = function(CopyConstructor, pooler) {
  var NewKlass = CopyConstructor;
  NewKlass.instancePool = [];
  NewKlass.getPooled = pooler || DEFAULT_POOLER;
  if (!NewKlass.poolSize) {
    NewKlass.poolSize = DEFAULT_POOL_SIZE;
  }
  NewKlass.release = standardReleaser;
  return NewKlass;
};

var PooledClass = {
  addPoolingTo: addPoolingTo,
  oneArgumentPooler: oneArgumentPooler,
  twoArgumentPooler: twoArgumentPooler,
  threeArgumentPooler: threeArgumentPooler,
  fourArgumentPooler: fourArgumentPooler,
  fiveArgumentPooler: fiveArgumentPooler,
};
```

見つかった。poolerなどは初期状態なので、デフォルトを見に行く。

```js:PooledClass(続き)
var DEFAULT_POOL_SIZE = 10;
var DEFAULT_POOLER = oneArgumentPooler;

var oneArgumentPooler = function(copyFieldsFrom) {
  var Klass = this;
  if (Klass.instancePool.length) {
    var instance = Klass.instancePool.pop();
    Klass.call(instance, copyFieldsFrom);
    return instance;
  } else {
    return new Klass(copyFieldsFrom);
  }
};

var standardReleaser = function(instance) {
  var Klass = this;
  invariant(
    instance instanceof Klass,
    'Trying to release an instance into a pool of a different type.'
  );
  instance.destructor();
  if (Klass.instancePool.length < Klass.poolSize) {
    Klass.instancePool.push(instance);
  }
};
```

`Klass=this=ReactReconcileTransaction`なのでReactReconcileTransactionのuseCreateElementに値を詰めるだけとなる。その値となるReactDOMFeatureFlags.useCreateElementを見に行く。

```js:ReactDOMFeatureFlags
var ReactDOMFeatureFlags = {
  useCreateElement: true,
};
```

useCreateElementを詰めるだけにこんなに処理を行っているのか。
ReactMountに戻って続きを見に行く。

```js:ReactMount.js
  transaction.perform(
    mountComponentIntoNode,
    null,
    componentInstance,
    container,
    transaction,
    shouldReuseMarkup,
    context
  );
```

`transaction=ReactReconcileTransaction`なのでReactReconcileTransactionがperformを持っているか見てみる。assign元のTransaction.Mixinにあるので見に行く。

```js:Transaction.js
  perform: function(method, scope, a, b, c, d, e, f) {
    ...,

    var errorThrown;
    var ret;
    try {
      this._isInTransaction = true;
      // Catching errors makes debugging more difficult, so we start with
      // errorThrown set to true before setting it to false after calling
      // close -- if it's still set to true in the finally block, it means
      // one of these calls threw.
      errorThrown = true;
      this.initializeAll(0);
      ret = method.call(scope, a, b, c, d, e, f);
      errorThrown = false;
    } finally {
      try {
        if (errorThrown) {
          // If `method` throws, prefer to show that stack trace over any thrown
          // by invoking `closeAll`.
          try {
            this.closeAll(0);
          } catch (err) {
          }
        } else {
          // Since `method` didn't throw, we don't want to silence the exception
          // here.
          this.closeAll(0);
        }
      } finally {
        this._isInTransaction = false;
      }
    }
    return ret;
  },
```

error handlingが長いが、method.callがの結果を取得するのが目的。methodとなる、mountComponentIntoNodeを見に行く。
続く。