Description
This one is hard to explain or reproduce but I'll try...
I'm using the following mixin which has changed slightly from the original I found in a gist a while back:
var React = require('react');
var LayeredComponentMixin = {
componentDidMount: function() {
this._layer = document.createElement('div');
document.body.appendChild(this._layer);
this._renderLayer();
},
componentDidUpdate: function() {
this._renderLayer();
},
componentWillUnmount: function() {
this._unrenderLayer();
document.body.removeChild(this._layer);
},
_renderLayer: function() {
var layer = this.renderLayer();
if (null === layer) {
layer = <noscript />;
}
React.render(layer, this._layer);
if (this.layerDidMount) {
this.layerDidMount(this._layer);
}
},
_unrenderLayer: function() {
if (this.layerWillUnmount) {
this.layerWillUnmount(this._layer);
}
React.unmountComponentAtNode(this._layer);
}
};
module.exports = LayeredComponentMixin;
This allows me to create layered components, in my case a modal dialog. I use the following component to build my modals:
var React = require('react');
var ToolboxUserActionCreators = require('../../actions/ToolboxUserActionCreators');
var ModalStore = require('../../stores/ModalStore');
function getStateFromStore() {
return {
modalCount: ModalStore.getModalCount()
};
}
var Modal = React.createClass({
componentWillMount: function() {
this.setState({
modalLevel: ModalStore.getModalCount()
});
},
componentDidMount: function() {
ModalStore.addChangeListener(this.onChange);
document.addEventListener('keydown', this.handleKeyDown);
},
componentWillUnmount: function() {
ModalStore.removeChangeListener(this.onChange);
document.removeEventListener('keydown', this.handleKeyDown);
},
getInitialState: function() {
return getStateFromStore();
},
getDefaultProps: function() {
return {
className: 'feature'
}
},
render: function() {
var className = 'toolbox2-modal-content toolbox2-modal-' + this.props.className;
var modalBackdropClassName = 'toolbox2-modal-backdrop';
var handleBackdropClick = this.handleBackdropClick;
var killClick = this.killClick;
var modalLevel = this.state.modalLevel;
var totalModals = this.state.modalCount;
if (modalLevel < totalModals) {
modalBackdropClassName += ' toolbox2-modal-backdrop-secondary';
}
return (
<div className={modalBackdropClassName} onClick={handleBackdropClick}>
<div className={className} onClick={killClick}>
{this.props.children}
</div>
</div>
);
},
killClick: function(e) {
e.stopPropagation();
},
handleBackdropClick: function() {
this.props.onRequestClose();
},
handleKeyDown: function(e) {
if (e.keyCode === 27) {
this.handleBackdropClick();
}
},
onChange: function() {
this.setState(getStateFromStore());
}
});
module.exports = Modal;
This combination works perfectly. That is until I close the highest modal. If an <input />
component is used inside of the <Modal>
render method, ie this.props.children
, the following error is thrown. All other element types (that I've tried including textarea
and select
work fine, but input
throws the following:
Uncaught Error: Invariant Violation: getDOMNode(): A component must be mounted to have a DOM node
The trace is as follows:
Uncaught Error: Invariant Violation: getDOMNode(): A component must be mounted to have a DOM node. bundle.js:60783
invariant bundle.js:60783
ReactBrowserComponentMixin.getDOMNode bundle.js:62069
ReactCompositeComponent.createClass.componentDidUpdate bundle.js:64334
assign.notifyAll bundle.js:68718
ON_DOM_READY_QUEUEING.close bundle.js:72258
Mixin.closeAll bundle.js:68960
Mixin.perform bundle.js:68901
Mixin.perform bundle.js:68887
assign.perform bundle.js:56107
(anonymous function) bundle.js:56186
wrapper bundle.js:53734
Mixin.closeAll bundle.js:68960
Mixin.perform bundle.js:68901
ReactDefaultBatchingStrategy.batchedUpdates bundle.js:64050
batchedUpdates bundle.js:56122
ReactEventListener.dispatchEvent bundle.js:64980
ReactCompositeComponent.createClass.componentDidUpdate bundle.js:64334
is here.
This only happens when there are several modals on top of each other; one dialog with an <input />
runs fine with no error.
The weird thing is that the app still runs fine, the error thrown has no impact (apart from being thrown) on how my app performs....
This is on 0.12RC1 using webpack to build.