見落としていた点
どうやらhtml要素をmountする段階でpropertyをupdateする時にtransactionをqueueに詰めているのそこを見てみる。
ReactDOMComponent.js
mountComponent: function(
transaction,
nativeParent,
nativeContainerInfo,
context
) {
...,
this._updateDOMProperties(null, props, transaction);
},
_updateDOMProperties: function(lastProps, nextProps, transaction) {
...,
for (propKey in nextProps) {
...,
if ... {
...,
} else if (registrationNameModules.hasOwnProperty(propKey)) {
if (nextProp) {
enqueuePutListener(this, propKey, nextProp, transaction);
} else if (lastProp) {
deleteListener(this, propKey)
} ...,
}
},
var registrationNameModules = EventPluginRegistry.registrationNameModules;
EventPluginRegistry.js
var EventPluginRegistry = {
...,
registrationNameModules: {},
};
空だが、実はinjectの最中に登録されていた。
function recomputePluginOrdering() {
...,
for (var pluginName in namesToPlugins) {
var PluginModule = namesToPlugins[pluginName];
...,
EventPluginRegistry.plugins[pluginIndex] = PluginModule;
var publishedEvents = PluginModule.eventTypes;
for (var eventName in publishedEvents) {
invariant(
publishEventForPlugin(
publishedEvents[eventName],
PluginModule,
eventName
),
'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
eventName,
pluginName
);
}
}
}
function publishEventForPlugin(dispatchConfig, PluginModule, eventName) {
...,
EventPluginRegistry.eventNameDispatchConfigs[eventName] = dispatchConfig;
var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
if (phasedRegistrationNames) {
for (var phaseName in phasedRegistrationNames) {
if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
var phasedRegistrationName = phasedRegistrationNames[phaseName];
publishRegistrationName(
phasedRegistrationName,
PluginModule,
eventName
);
}
}
return true;
} else if (dispatchConfig.registrationName) {
publishRegistrationName(
dispatchConfig.registrationName,
PluginModule,
eventName
);
return true;
}
return false;
}
function publishRegistrationName(registrationName, PluginModule, eventName) {
...,
EventPluginRegistry.registrationNameModules[registrationName] = PluginModule;
EventPluginRegistry.registrationNameDependencies[registrationName] =
PluginModule.eventTypes[eventName].dependencies;
...,
}
namesToPlugins
namesToPlugins = {
SimpleEventPlugin: SimpleEventPlugin,
EnterLeaveEventPlugin: EnterLeaveEventPlugin,
ChangeEventPlugin: ChangeEventPlugin,
SelectEventPlugin: SelectEventPlugin,
BeforeInputEventPlugin: BeforeInputEventPlugin,
}
SimpleEventPlugin
var eventTypes = {
...,
click: {
phasedRegistrationNames: {
bubbled: keyOf({onClick: true}),
captured: keyOf({onClickCapture: true}),
},
},
...,
};
var SimpleEventPlugin = {
eventTypes: eventTypes,
...,
};
keyOf
var keyOf = function(oneKeyObj) {
var key;
for (key in oneKeyObj) {
if (!oneKeyObj.hasOwnProperty(key)) {
continue;
}
return key;
}
return null;
};
keyだけを取り出すfunciton。
ここまでで、registrationNameModules['onClick']=SimpleEventPlugin
となる。
あとはonChangeなどの基本的なイベントがPluginによって提供されている。
EventHandler用の分岐処理ということがわかった。
よって、name
やtext
などのpropertyは最後の分岐を通る。
ReactDOMComponent.js
function isCustomComponent(tagName, props) {
return tagName.indexOf('-') >= 0 || props.is != null;
}
...,
_updateDOMProperties: function(lastProps, nextProps, transaction) {
...,
for (propKey in nextProps) {
...,
if ... {
...,
} else if ..., {
...,
} else if (isCustomComponent(this._tag, nextProps)) {
...,
} else if ..., {
...,
}
}
},
-
をtagが含んでいればCusotmComponentと判断されるようだ。
ReactDOMComponent.js
_updateDOMProperties: function(lastProps, nextProps, transaction) {
...,
for (propKey in nextProps) {
...,
if ... {
...,
} else if ..., {
...,
} else if ..., {
...,
} else if
DOMProperty.properties[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
var node = getNode(this);
// If we're updating to null or undefined, we should remove the property
// from the DOM node instead of inadvertently setting to a string. This
// brings us in line with the same behavior we have on initial render.
if (nextProp != null) {
DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);
} else {
DOMPropertyOperations.deleteValueForProperty(node, propKey);
}
}
}
},
DOMProperty.properties = HTMLDOMPropertyConfig.Properties = {
// 色々ある
}
DOMProperty.properties = SVGDOMPropertyConfig.Properties = {
// 色々ある
// attributeに実際のpropertyは含まれている
// property自体はすべて0が詰められている
}
DOMPropertyOperations.js
var DOMPropertyOperations = {
...,
setValueForProperty: function(node, name, value) {
...,
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ?
DOMProperty.properties[name] : null;
if (propertyInfo) {
var mutationMethod = propertyInfo.mutationMethod;
if (mutationMethod) {
mutationMethod(node, value);
} else if (shouldIgnoreValue(propertyInfo, value)) {
this.deleteValueForProperty(node, name);
} else if (propertyInfo.mustUseProperty) {
var propName = propertyInfo.propertyName;
// Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the
// property type before comparing; only `value` does and is string.
if (!propertyInfo.hasSideEffects ||
('' + node[propName]) !== ('' + value)) {
// Contrary to `setAttribute`, object properties are properly
// `toString`ed by IE8/9.
node[propName] = value;
}
} else {
...,
}
} else if ... {
...,
}
},
...,
};
DOMProperty.js
var DOMPropertyInjection = {
...,
var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};
...,
if (DOMMutationMethods.hasOwnProperty(propName)) {
propertyInfo.mutationMethod = DOMMutationMethods[propName];
}
...,
};
DOMMutationMethodsはnullなので無視。
node.setAttribute(attributeName, '' + value);
普通のpropertyの場合はこちら。
node = getNode(ReactDOMComponent) = ReactDOMComponentTree.getNodeFromInstance(ReactDOMComponent)
ReactDOMComponentTree.js
function getInstanceFromNode(node) {
var inst = getClosestInstanceFromNode(node);
if (inst != null && inst._nativeNode === node) {
return inst;
} else {
return null;
}
}
var ReactDOMComponentTree = {
...,
getNodeFromInstance: getNodeFromInstance,
...,
};
ReactDOMComponentTree.js
function getClosestInstanceFromNode(node) {
if (node[internalInstanceKey]) {
return node[internalInstanceKey];
}
// Walk up the tree until we find an ancestor whose instance we have cached.
var parents = [];
while (!node[internalInstanceKey]) {
parents.push(node);
if (node.parentNode) {
node = node.parentNode;
} else {
// Top of the tree. This node must not be part of a React tree (or is
// unmounted, potentially).
return null;
}
}
var closest;
var inst;
for (; node && (inst = node[internalInstanceKey]); node = parents.pop()) {
closest = inst;
if (parents.length) {
precacheChildNodes(inst, node);
}
}
return closest;
}
internalInstance(wrapされたComponent)を持つ親があればそれを返す。
なければ持つ親を探して、それをキャッシュして返すだけ。
最終的にはnodeのpropsに値を詰めているだけのように思える。