mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Make DOM wrapper component using lower-level primitives
Introducing: a really lame version of composite components, right inside of ReactDOMComponent! Now ReactDOMInput isn't an actual component. This brings us closer to exposing DOM nodes as refs.
This commit is contained in:
1 parent
db82ed09c5
commit
52a229f168
10 files changed
+268
-196
No files matched your search
+13
-3
@@ -6,16 +6,18 @@
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule AutoFocusMixin
|
||||
* @providesModule AutoFocusUtils
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactMount = require('ReactMount');
|
||||
|
||||
var findDOMNode = require('findDOMNode');
|
||||
var focusNode = require('focusNode');
|
||||
|
||||
var AutoFocusMixin = {
|
||||
var Mixin = {
|
||||
componentDidMount: function() {
|
||||
if (this.props.autoFocus) {
|
||||
focusNode(findDOMNode(this));
|
||||
@@ -23,4 +25,12 @@ var AutoFocusMixin = {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = AutoFocusMixin;
|
||||
var AutoFocusUtils = {
|
||||
Mixin: Mixin,
|
||||
|
||||
focusDOMComponent: function() {
|
||||
focusNode(ReactMount.getNode(this._rootNodeID));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = AutoFocusUtils;
|
||||
@@ -15,6 +15,7 @@
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var warning = require('warning');
|
||||
|
||||
var hasReadOnlyValue = {
|
||||
'button': true,
|
||||
@@ -52,20 +53,48 @@ function _assertCheckedLink(inputProps) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SyntheticEvent} e change event to handle
|
||||
*/
|
||||
function _handleLinkedValueChange(e) {
|
||||
/*jshint validthis:true */
|
||||
this.props.valueLink.requestChange(e.target.value);
|
||||
}
|
||||
var propTypes = {
|
||||
value: function(props, propName, componentName) {
|
||||
if (!props[propName] ||
|
||||
hasReadOnlyValue[props.type] ||
|
||||
props.onChange ||
|
||||
props.readOnly ||
|
||||
props.disabled) {
|
||||
return null;
|
||||
}
|
||||
return new Error(
|
||||
'You provided a `value` prop to a form field without an ' +
|
||||
'`onChange` handler. This will render a read-only field. If ' +
|
||||
'the field should be mutable use `defaultValue`. Otherwise, ' +
|
||||
'set either `onChange` or `readOnly`.'
|
||||
);
|
||||
},
|
||||
checked: function(props, propName, componentName) {
|
||||
if (!props[propName] ||
|
||||
props.onChange ||
|
||||
props.readOnly ||
|
||||
props.disabled) {
|
||||
return null;
|
||||
}
|
||||
return new Error(
|
||||
'You provided a `checked` prop to a form field without an ' +
|
||||
'`onChange` handler. This will render a read-only field. If ' +
|
||||
'the field should be mutable use `defaultChecked`. Otherwise, ' +
|
||||
'set either `onChange` or `readOnly`.'
|
||||
);
|
||||
},
|
||||
onChange: ReactPropTypes.func
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SyntheticEvent} e change event to handle
|
||||
*/
|
||||
function _handleLinkedCheckChange(e) {
|
||||
/*jshint validthis:true */
|
||||
this.props.checkedLink.requestChange(e.target.checked);
|
||||
var loggedTypeFailures = {};
|
||||
function getDeclarationErrorAddendum(owner) {
|
||||
if (owner) {
|
||||
var name = owner.getName();
|
||||
if (name) {
|
||||
return ' Check the render method of `' + name + '`.';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,38 +102,19 @@ function _handleLinkedCheckChange(e) {
|
||||
* this outside of the ReactDOM controlled form components.
|
||||
*/
|
||||
var LinkedValueUtils = {
|
||||
Mixin: {
|
||||
propTypes: {
|
||||
value: function(props, propName, componentName) {
|
||||
if (!props[propName] ||
|
||||
hasReadOnlyValue[props.type] ||
|
||||
props.onChange ||
|
||||
props.readOnly ||
|
||||
props.disabled) {
|
||||
return null;
|
||||
}
|
||||
return new Error(
|
||||
'You provided a `value` prop to a form field without an ' +
|
||||
'`onChange` handler. This will render a read-only field. If ' +
|
||||
'the field should be mutable use `defaultValue`. Otherwise, ' +
|
||||
'set either `onChange` or `readOnly`.'
|
||||
);
|
||||
},
|
||||
checked: function(props, propName, componentName) {
|
||||
if (!props[propName] ||
|
||||
props.onChange ||
|
||||
props.readOnly ||
|
||||
props.disabled) {
|
||||
return null;
|
||||
}
|
||||
return new Error(
|
||||
'You provided a `checked` prop to a form field without an ' +
|
||||
'`onChange` handler. This will render a read-only field. If ' +
|
||||
'the field should be mutable use `defaultChecked`. Otherwise, ' +
|
||||
'set either `onChange` or `readOnly`.'
|
||||
);
|
||||
},
|
||||
onChange: ReactPropTypes.func
|
||||
checkPropTypes: function (tagName, props, owner) {
|
||||
for (var propName in propTypes) {
|
||||
if (propTypes.hasOwnProperty(propName)) {
|
||||
var error = propTypes[propName](props, propName, tagName, location);
|
||||
}
|
||||
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
|
||||
// Only monitor this failure once because there tends to be a lot of the
|
||||
// same error.
|
||||
loggedTypeFailures[error.message] = true;
|
||||
|
||||
var addendum = getDeclarationErrorAddendum(owner);
|
||||
warning(false, 'Failed form propType: %s%s', error.message, addendum);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -135,17 +145,18 @@ var LinkedValueUtils = {
|
||||
|
||||
/**
|
||||
* @param {object} inputProps Props for form component
|
||||
* @return {function} change callback either from onChange prop or link.
|
||||
* @param {SyntheticEvent} event change event to handle
|
||||
*/
|
||||
getOnChange: function(inputProps) {
|
||||
executeOnChange: function(inputProps, event) {
|
||||
if (inputProps.valueLink) {
|
||||
_assertValueLink(inputProps);
|
||||
return _handleLinkedValueChange;
|
||||
return inputProps.valueLink.requestChange(event.target.value);
|
||||
} else if (inputProps.checkedLink) {
|
||||
_assertCheckedLink(inputProps);
|
||||
return _handleLinkedCheckChange;
|
||||
return inputProps.checkedLink.requestChange(event.target.checked);
|
||||
} else if (inputProps.onChange) {
|
||||
return inputProps.onChange.call(undefined, event);
|
||||
}
|
||||
return inputProps.onChange;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var AutoFocusMixin = require('AutoFocusMixin');
|
||||
var AutoFocusUtils = require('AutoFocusUtils');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
@@ -41,7 +41,7 @@ var ReactDOMButton = ReactClass.createClass({
|
||||
displayName: 'ReactDOMButton',
|
||||
tagName: 'BUTTON',
|
||||
|
||||
mixins: [AutoFocusMixin, ReactBrowserComponentMixin],
|
||||
mixins: [AutoFocusUtils.Mixin, ReactBrowserComponentMixin],
|
||||
|
||||
render: function() {
|
||||
var props = {};
|
||||
|
||||
@@ -11,27 +11,22 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var AutoFocusMixin = require('AutoFocusMixin');
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var AutoFocusUtils = require('AutoFocusUtils');
|
||||
var ReactDOMIDOperations = require('ReactDOMIDOperations');
|
||||
var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
var findDOMNode = require('findDOMNode');
|
||||
var invariant = require('invariant');
|
||||
|
||||
var input = ReactElement.createFactory('input');
|
||||
|
||||
var instancesByReactID = {};
|
||||
|
||||
function forceUpdateIfMounted() {
|
||||
/*jshint validthis:true */
|
||||
if (this.isMounted()) {
|
||||
this.forceUpdate();
|
||||
if (this._rootNodeID) {
|
||||
// DOM component is still mounted; update
|
||||
ReactDOMInput.updateWrapper(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,124 +46,132 @@ function forceUpdateIfMounted() {
|
||||
*
|
||||
* @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
|
||||
*/
|
||||
var ReactDOMInput = ReactClass.createClass({
|
||||
displayName: 'ReactDOMInput',
|
||||
tagName: 'INPUT',
|
||||
var ReactDOMInput = {
|
||||
getNativeProps: function(inst, props, context) {
|
||||
var value = LinkedValueUtils.getValue(props);
|
||||
var checked = LinkedValueUtils.getChecked(props);
|
||||
|
||||
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],
|
||||
var nativeProps = assign({}, props, {
|
||||
defaultChecked: undefined,
|
||||
defaultValue: undefined,
|
||||
value: value != null ? value : inst._wrapperState.initialValue,
|
||||
checked: checked != null ? checked : inst._wrapperState.initialChecked,
|
||||
onChange: inst._wrapperState.onChange
|
||||
});
|
||||
|
||||
getInitialState: function() {
|
||||
var defaultValue = this.props.defaultValue;
|
||||
return {
|
||||
initialChecked: this.props.defaultChecked || false,
|
||||
initialValue: defaultValue != null ? defaultValue : null
|
||||
return nativeProps;
|
||||
},
|
||||
|
||||
mountWrapper: function(inst, props) {
|
||||
LinkedValueUtils.checkPropTypes(
|
||||
'input',
|
||||
props,
|
||||
inst._currentElement._owner
|
||||
);
|
||||
|
||||
var defaultValue = props.defaultValue;
|
||||
inst._wrapperState = {
|
||||
initialChecked: props.defaultChecked || false,
|
||||
initialValue: defaultValue != null ? defaultValue : null,
|
||||
onChange: _handleChange.bind(inst)
|
||||
};
|
||||
|
||||
instancesByReactID[inst._rootNodeID] = inst;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// Clone `this.props` so we don't mutate the input.
|
||||
var props = assign({}, this.props);
|
||||
|
||||
props.defaultChecked = null;
|
||||
props.defaultValue = null;
|
||||
|
||||
var value = LinkedValueUtils.getValue(this.props);
|
||||
props.value = value != null ? value : this.state.initialValue;
|
||||
|
||||
var checked = LinkedValueUtils.getChecked(this.props);
|
||||
props.checked = checked != null ? checked : this.state.initialChecked;
|
||||
|
||||
props.onChange = this._handleChange;
|
||||
|
||||
return input(props, this.props.children);
|
||||
postMountWrapper: function(inst, transaction, props) {
|
||||
if (props.autoFocus) {
|
||||
transaction.getReactMountReady().enqueue(
|
||||
AutoFocusUtils.focusDOMComponent,
|
||||
inst
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
var id = ReactMount.getID(findDOMNode(this));
|
||||
instancesByReactID[id] = this;
|
||||
unmountWrapper: function(inst) {
|
||||
delete instancesByReactID[inst._rootNodeID];
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
var rootNode = findDOMNode(this);
|
||||
var id = ReactMount.getID(rootNode);
|
||||
delete instancesByReactID[id];
|
||||
},
|
||||
updateWrapper: function(inst) {
|
||||
var props = inst._currentElement.props;
|
||||
|
||||
componentDidUpdate: function(prevProps, prevState, prevContext) {
|
||||
var rootNode = findDOMNode(this);
|
||||
if (this.props.checked != null) {
|
||||
DOMPropertyOperations.setValueForProperty(
|
||||
rootNode,
|
||||
// TODO: Shouldn't this be getChecked(props)?
|
||||
var checked = props.checked;
|
||||
if (checked != null) {
|
||||
ReactDOMIDOperations.updatePropertyByID(
|
||||
inst._rootNodeID,
|
||||
'checked',
|
||||
this.props.checked || false
|
||||
checked || false
|
||||
);
|
||||
}
|
||||
|
||||
var value = LinkedValueUtils.getValue(this.props);
|
||||
var value = LinkedValueUtils.getValue(props);
|
||||
if (value != null) {
|
||||
// Cast `value` to a string to ensure the value is set correctly. While
|
||||
// browsers typically do this as necessary, jsdom doesn't.
|
||||
DOMPropertyOperations.setValueForProperty(rootNode, 'value', '' + value);
|
||||
ReactDOMIDOperations.updatePropertyByID(
|
||||
inst._rootNodeID,
|
||||
'value',
|
||||
'' + value
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
_handleChange: function(event) {
|
||||
var returnValue;
|
||||
var onChange = LinkedValueUtils.getOnChange(this.props);
|
||||
if (onChange) {
|
||||
returnValue = onChange.call(this, event);
|
||||
function _handleChange(event) {
|
||||
var props = this._currentElement.props;
|
||||
|
||||
var returnValue = LinkedValueUtils.executeOnChange(props, event);
|
||||
|
||||
// Here we use asap to wait until all updates have propagated, which
|
||||
// is important when using controlled components within layers:
|
||||
// https://github.com/facebook/react/issues/1698
|
||||
ReactUpdates.asap(forceUpdateIfMounted, this);
|
||||
|
||||
var name = props.name;
|
||||
if (props.type === 'radio' && name != null) {
|
||||
var rootNode = ReactMount.getNode(this._rootNodeID);
|
||||
var queryRoot = rootNode;
|
||||
|
||||
while (queryRoot.parentNode) {
|
||||
queryRoot = queryRoot.parentNode;
|
||||
}
|
||||
// Here we use asap to wait until all updates have propagated, which
|
||||
// is important when using controlled components within layers:
|
||||
// https://github.com/facebook/react/issues/1698
|
||||
ReactUpdates.asap(forceUpdateIfMounted, this);
|
||||
|
||||
var name = this.props.name;
|
||||
if (this.props.type === 'radio' && name != null) {
|
||||
var rootNode = findDOMNode(this);
|
||||
var queryRoot = rootNode;
|
||||
// If `rootNode.form` was non-null, then we could try `form.elements`,
|
||||
// but that sometimes behaves strangely in IE8. We could also try using
|
||||
// `form.getElementsByName`, but that will only return direct children
|
||||
// and won't include inputs that use the HTML5 `form=` attribute. Since
|
||||
// the input might not even be in a form, let's just use the global
|
||||
// `querySelectorAll` to ensure we don't miss anything.
|
||||
var group = queryRoot.querySelectorAll(
|
||||
'input[name=' + JSON.stringify('' + name) + '][type="radio"]');
|
||||
|
||||
while (queryRoot.parentNode) {
|
||||
queryRoot = queryRoot.parentNode;
|
||||
}
|
||||
|
||||
// If `rootNode.form` was non-null, then we could try `form.elements`,
|
||||
// but that sometimes behaves strangely in IE8. We could also try using
|
||||
// `form.getElementsByName`, but that will only return direct children
|
||||
// and won't include inputs that use the HTML5 `form=` attribute. Since
|
||||
// the input might not even be in a form, let's just use the global
|
||||
// `querySelectorAll` to ensure we don't miss anything.
|
||||
var group = queryRoot.querySelectorAll(
|
||||
'input[name=' + JSON.stringify('' + name) + '][type="radio"]');
|
||||
|
||||
for (var i = 0; i < group.length; i++) {
|
||||
var otherNode = group[i];
|
||||
if (otherNode === rootNode ||
|
||||
otherNode.form !== rootNode.form) {
|
||||
continue;
|
||||
}
|
||||
var otherID = ReactMount.getID(otherNode);
|
||||
invariant(
|
||||
otherID,
|
||||
'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
|
||||
'same `name` is not supported.'
|
||||
);
|
||||
var otherInstance = instancesByReactID[otherID];
|
||||
invariant(
|
||||
otherInstance,
|
||||
'ReactDOMInput: Unknown radio button ID %s.',
|
||||
otherID
|
||||
);
|
||||
// If this is a controlled radio button group, forcing the input that
|
||||
// was previously checked to update will cause it to be come re-checked
|
||||
// as appropriate.
|
||||
ReactUpdates.asap(forceUpdateIfMounted, otherInstance);
|
||||
for (var i = 0; i < group.length; i++) {
|
||||
var otherNode = group[i];
|
||||
if (otherNode === rootNode ||
|
||||
otherNode.form !== rootNode.form) {
|
||||
continue;
|
||||
}
|
||||
var otherID = ReactMount.getID(otherNode);
|
||||
invariant(
|
||||
otherID,
|
||||
'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
|
||||
'same `name` is not supported.'
|
||||
);
|
||||
var otherInstance = instancesByReactID[otherID];
|
||||
invariant(
|
||||
otherInstance,
|
||||
'ReactDOMInput: Unknown radio button ID %s.',
|
||||
otherID
|
||||
);
|
||||
// If this is a controlled radio button group, forcing the input that
|
||||
// was previously checked to update will cause it to be come re-checked
|
||||
// as appropriate.
|
||||
ReactUpdates.asap(forceUpdateIfMounted, otherInstance);
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
});
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
module.exports = ReactDOMInput;
|
||||
@@ -11,11 +11,12 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var AutoFocusMixin = require('AutoFocusMixin');
|
||||
var AutoFocusUtils = require('AutoFocusUtils');
|
||||
var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
|
||||
@@ -118,7 +119,7 @@ var ReactDOMSelect = ReactClass.createClass({
|
||||
displayName: 'ReactDOMSelect',
|
||||
tagName: 'SELECT',
|
||||
|
||||
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],
|
||||
mixins: [AutoFocusUtils.Mixin, ReactBrowserComponentMixin],
|
||||
|
||||
statics: {
|
||||
valueContextKey: valueContextKey
|
||||
@@ -129,6 +130,16 @@ var ReactDOMSelect = ReactClass.createClass({
|
||||
value: selectValueType
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
LinkedValueUtils.checkPropTypes(
|
||||
'select',
|
||||
this.props,
|
||||
ReactInstanceMap.get(this)._currentElement._owner
|
||||
);
|
||||
|
||||
this._pendingUpdate = false;
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
// Pass down initial value so initial generated markup has correct
|
||||
// `selected` attributes
|
||||
@@ -162,10 +173,6 @@ var ReactDOMSelect = ReactClass.createClass({
|
||||
return select(props, this.props.children);
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._pendingUpdate = false;
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
// After the initial mount, we control selected-ness manually so don't pass
|
||||
// the context value down
|
||||
@@ -189,11 +196,7 @@ var ReactDOMSelect = ReactClass.createClass({
|
||||
},
|
||||
|
||||
_handleChange: function(event) {
|
||||
var returnValue;
|
||||
var onChange = LinkedValueUtils.getOnChange(this.props);
|
||||
if (onChange) {
|
||||
returnValue = onChange.call(this, event);
|
||||
}
|
||||
var returnValue = LinkedValueUtils.executeOnChange(this.props, event);
|
||||
|
||||
this._pendingUpdate = true;
|
||||
ReactUpdates.asap(updateOptionsIfPendingUpdateAndMounted, this);
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var AutoFocusMixin = require('AutoFocusMixin');
|
||||
var AutoFocusUtils = require('AutoFocusUtils');
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
@@ -53,7 +54,15 @@ var ReactDOMTextarea = ReactClass.createClass({
|
||||
displayName: 'ReactDOMTextarea',
|
||||
tagName: 'TEXTAREA',
|
||||
|
||||
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],
|
||||
mixins: [AutoFocusUtils.Mixin, ReactBrowserComponentMixin],
|
||||
|
||||
componentWillMount: function() {
|
||||
LinkedValueUtils.checkPropTypes(
|
||||
'textarea',
|
||||
this.props,
|
||||
ReactInstanceMap.get(this)._currentElement._owner
|
||||
);
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var defaultValue = this.props.defaultValue;
|
||||
@@ -123,11 +132,7 @@ var ReactDOMTextarea = ReactClass.createClass({
|
||||
},
|
||||
|
||||
_handleChange: function(event) {
|
||||
var returnValue;
|
||||
var onChange = LinkedValueUtils.getOnChange(this.props);
|
||||
if (onChange) {
|
||||
returnValue = onChange.call(this, event);
|
||||
}
|
||||
var returnValue = LinkedValueUtils.executeOnChange(this.props, event);
|
||||
ReactUpdates.asap(forceUpdateIfMounted, this);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ var HTMLDOMPropertyConfig = {
|
||||
alt: null,
|
||||
async: HAS_BOOLEAN_VALUE,
|
||||
autoComplete: null,
|
||||
// autoFocus is polyfilled/normalized by AutoFocusMixin
|
||||
// autoFocus is polyfilled/normalized by AutoFocusUtils
|
||||
// autoFocus: HAS_BOOLEAN_VALUE,
|
||||
autoPlay: HAS_BOOLEAN_VALUE,
|
||||
capture: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
||||
|
||||
@@ -20,6 +20,7 @@ var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
||||
var ReactComponentBrowserEnvironment =
|
||||
require('ReactComponentBrowserEnvironment');
|
||||
var ReactDOMInput = require('ReactDOMInput');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactMultiChild = require('ReactMultiChild');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
@@ -254,6 +255,7 @@ function ReactDOMComponent(tag) {
|
||||
this._previousStyle = null;
|
||||
this._previousStyleCopy = null;
|
||||
this._rootNodeID = null;
|
||||
this._wrapperState = null;
|
||||
}
|
||||
|
||||
ReactDOMComponent.displayName = 'ReactDOMComponent';
|
||||
@@ -277,7 +279,16 @@ ReactDOMComponent.Mixin = {
|
||||
mountComponent: function(rootID, transaction, context) {
|
||||
this._rootNodeID = rootID;
|
||||
|
||||
assertValidProps(this, this._currentElement.props);
|
||||
var props = this._currentElement.props;
|
||||
|
||||
switch (this._tag) {
|
||||
case 'input':
|
||||
ReactDOMInput.mountWrapper(this, props);
|
||||
props = ReactDOMInput.getNativeProps(this, props, context);
|
||||
break;
|
||||
}
|
||||
|
||||
assertValidProps(this, props);
|
||||
if (__DEV__) {
|
||||
if (context[validateDOMNesting.ancestorInfoContextKey]) {
|
||||
validateDOMNesting(
|
||||
@@ -288,8 +299,15 @@ ReactDOMComponent.Mixin = {
|
||||
}
|
||||
}
|
||||
|
||||
var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction);
|
||||
var tagContent = this._createContentMarkup(transaction, context);
|
||||
var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);
|
||||
var tagContent = this._createContentMarkup(transaction, props, context);
|
||||
|
||||
switch (this._tag) {
|
||||
case 'input':
|
||||
ReactDOMInput.postMountWrapper(this, transaction, props);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!tagContent && omittedCloseTags[this._tag]) {
|
||||
return tagOpen + '/>';
|
||||
}
|
||||
@@ -306,10 +324,10 @@ ReactDOMComponent.Mixin = {
|
||||
*
|
||||
* @private
|
||||
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
||||
* @param {object} props
|
||||
* @return {string} Markup of opening tag.
|
||||
*/
|
||||
_createOpenTagMarkupAndPutListeners: function(transaction) {
|
||||
var props = this._currentElement.props;
|
||||
_createOpenTagMarkupAndPutListeners: function(transaction, props) {
|
||||
var ret = '<' + this._tag;
|
||||
|
||||
for (var propKey in props) {
|
||||
@@ -356,12 +374,12 @@ ReactDOMComponent.Mixin = {
|
||||
*
|
||||
* @private
|
||||
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
||||
* @param {object} props
|
||||
* @param {object} context
|
||||
* @return {string} Content markup.
|
||||
*/
|
||||
_createContentMarkup: function(transaction, context) {
|
||||
_createContentMarkup: function(transaction, props, context) {
|
||||
var ret = '';
|
||||
var props = this._currentElement.props;
|
||||
|
||||
// Intentional use of != to avoid catching zero/false.
|
||||
var innerHTML = props.dangerouslySetInnerHTML;
|
||||
@@ -427,10 +445,22 @@ ReactDOMComponent.Mixin = {
|
||||
* @overridable
|
||||
*/
|
||||
updateComponent: function(transaction, prevElement, nextElement, context) {
|
||||
assertValidProps(this, this._currentElement.props);
|
||||
this._updateDOMProperties(prevElement.props, transaction);
|
||||
var lastProps = prevElement.props;
|
||||
var nextProps = this._currentElement.props;
|
||||
|
||||
switch (this._tag) {
|
||||
case 'input':
|
||||
ReactDOMInput.updateWrapper(this);
|
||||
lastProps = ReactDOMInput.getNativeProps(this, lastProps);
|
||||
nextProps = ReactDOMInput.getNativeProps(this, nextProps);
|
||||
break;
|
||||
}
|
||||
|
||||
assertValidProps(this, nextProps);
|
||||
this._updateDOMProperties(lastProps, nextProps, transaction);
|
||||
this._updateDOMChildren(
|
||||
prevElement.props,
|
||||
lastProps,
|
||||
nextProps,
|
||||
transaction,
|
||||
processChildContext(context, this)
|
||||
);
|
||||
@@ -449,10 +479,10 @@ ReactDOMComponent.Mixin = {
|
||||
*
|
||||
* @private
|
||||
* @param {object} lastProps
|
||||
* @param {object} nextProps
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
*/
|
||||
_updateDOMProperties: function(lastProps, transaction) {
|
||||
var nextProps = this._currentElement.props;
|
||||
_updateDOMProperties: function(lastProps, nextProps, transaction) {
|
||||
var propKey;
|
||||
var styleName;
|
||||
var styleUpdates;
|
||||
@@ -558,11 +588,11 @@ ReactDOMComponent.Mixin = {
|
||||
* children content.
|
||||
*
|
||||
* @param {object} lastProps
|
||||
* @param {object} nextProps
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {object} context
|
||||
*/
|
||||
_updateDOMChildren: function(lastProps, transaction, context) {
|
||||
var nextProps = this._currentElement.props;
|
||||
|
||||
_updateDOMChildren: function(lastProps, nextProps, transaction, context) {
|
||||
var lastContent =
|
||||
CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;
|
||||
var nextContent =
|
||||
@@ -612,10 +642,17 @@ ReactDOMComponent.Mixin = {
|
||||
* @internal
|
||||
*/
|
||||
unmountComponent: function() {
|
||||
switch (this._tag) {
|
||||
case 'input':
|
||||
ReactDOMInput.unmountWrapper(this);
|
||||
break;
|
||||
}
|
||||
|
||||
this.unmountChildren();
|
||||
ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID);
|
||||
ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID);
|
||||
this._rootNodeID = null;
|
||||
this._wrapperState = null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -29,7 +29,6 @@ var ReactDOMForm = require('ReactDOMForm');
|
||||
var ReactDOMImg = require('ReactDOMImg');
|
||||
var ReactDOMIDOperations = require('ReactDOMIDOperations');
|
||||
var ReactDOMIframe = require('ReactDOMIframe');
|
||||
var ReactDOMInput = require('ReactDOMInput');
|
||||
var ReactDOMOption = require('ReactDOMOption');
|
||||
var ReactDOMSelect = require('ReactDOMSelect');
|
||||
var ReactDOMTextarea = require('ReactDOMTextarea');
|
||||
@@ -120,7 +119,6 @@ function inject() {
|
||||
'form': ReactDOMForm,
|
||||
'iframe': ReactDOMIframe,
|
||||
'img': ReactDOMImg,
|
||||
'input': ReactDOMInput,
|
||||
'option': ReactDOMOption,
|
||||
'select': ReactDOMSelect,
|
||||
'textarea': ReactDOMTextarea,
|
||||
|
||||
@@ -308,7 +308,8 @@ describe('ReactDOMComponent', function() {
|
||||
genMarkup = function(props) {
|
||||
var transaction = new ReactReconcileTransaction();
|
||||
return (new NodeStub(props))._createOpenTagMarkupAndPutListeners(
|
||||
transaction
|
||||
transaction,
|
||||
props
|
||||
);
|
||||
};
|
||||
|
||||
@@ -357,7 +358,11 @@ describe('ReactDOMComponent', function() {
|
||||
|
||||
genMarkup = function(props) {
|
||||
var transaction = new ReactReconcileTransaction();
|
||||
return (new NodeStub(props))._createContentMarkup(transaction, {});
|
||||
return (new NodeStub(props))._createContentMarkup(
|
||||
transaction,
|
||||
props,
|
||||
{}
|
||||
);
|
||||
};
|
||||
|
||||
this.addMatchers({
|
||||
|
||||
Reference in new issue
Block a user