LoginSignup
5
5

More than 5 years have passed since last update.

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

Posted at

JSX

  • 公式サイト
  • JSXの<Type />はReact.createElement(Type, attribute, children)に変換されるだけだった

ReactDOM続き

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

ReactDOM.js
ReactDefaultInjection.inject();

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

・・・先にinjectが実行されていた!よかった(涙)

ReactDefaultInjection.js
function inject() {

  ...,

  ReactInjection.Updates.injectBatchingStrategy(
    ReactDefaultBatchingStrategy
  );
} 

ReactInjectionを見に行く。

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を見てみる。

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を見に行く。

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を見に行く。

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されていた。

ReactDefaultInjection.js
  ...,

  ReactInjection.Updates.injectReconcileTransaction(
    ReactReconcileTransaction
  );

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

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

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を見に行く。

Transaction
var Mixin = {
  ...,
}

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

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などは初期状態なので、デフォルトを見に行く。

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を見に行く。

ReactDOMFeatureFlags
var ReactDOMFeatureFlags = {
  useCreateElement: true,
};

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

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

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

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を見に行く。
続く。

5
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
5
5