JSX
- 公式サイト
- JSXの
<Type />
はReact.createElement(Type, attribute, children)に変換されるだけだった
ReactDOM続き
JSXは特にinjectをしていないようだ。。さて、困った。
さて、ReactDOMでももう一度見てみるか・・
ReactDefaultInjection.inject();
var render = ReactPerf.measure('React', 'render', ReactMount.render);
・・・先にinjectが実行されていた!よかった(涙)
function inject() {
...,
ReactInjection.Updates.injectBatchingStrategy(
ReactDefaultBatchingStrategy
);
}
ReactInjectionを見に行く。
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を見てみる。
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を見に行く。
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を見に行く。
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されていた。
...,
ReactInjection.Updates.injectReconcileTransaction(
ReactReconcileTransaction
);
...,
var ReactUpdatesInjection = {
injectReconcileTransaction: function(ReconcileTransaction) {
invariant(
ReconcileTransaction,
'ReactUpdates: must provide a reconcile transaction class'
);
ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;
},
...,
}
ReactUpdates.ReactReconcileTransactionとなるReconcileTransactionを見てみる。
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を見に行く。
var Mixin = {
...,
}
それっぽいのがないので、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などは初期状態なので、デフォルトを見に行く。
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を見に行く。
var ReactDOMFeatureFlags = {
useCreateElement: true,
};
useCreateElementを詰めるだけにこんなに処理を行っているのか。
ReactMountに戻って続きを見に行く。
transaction.perform(
mountComponentIntoNode,
null,
componentInstance,
container,
transaction,
shouldReuseMarkup,
context
);
transaction=ReactReconcileTransaction
なのでReactReconcileTransactionがperformを持っているか見てみる。assign元のTransaction.Mixinにあるので見に行く。
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を見に行く。
続く。