I want to create a simple flow, with 2 components. The first component is rendered, I click a button on it and this action render the second component. Clicking on the button from the second component, it should switch back to the first one, but instead, an error occurred:
Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of
exports
.warning @ react.js:20728ReactElementValidator.createElement @ react.js:9853t.exports.React.createClass.render @ bundle.js:1ReactCompositeComponentMixin._renderValidatedComponentWithoutOwnerOrContext @ react.js:6330ReactCompositeComponentMixin._renderValidatedComponent @ react.js:6350wrapper @ react.js:12868ReactCompositeComponentMixin._updateRenderedComponent @ react.js:6303ReactCompositeComponentMixin._performComponentUpdate @ react.js:6287ReactCompositeComponentMixin.updateComponent @ react.js:6216wrapper @ react.js:12868ReactCompositeComponentMixin.performUpdateIfNecessary @ react.js:6164ReactReconciler.performUpdateIfNecessary @ react.js:13667runBatchedUpdates @ react.js:15356Mixin.perform @ react.js:17245Mixin.perform @ react.js:17245assign.perform @ react.js:15313flushBatchedUpdates @ react.js:15374wrapper @ react.js:12868Mixin.closeAll @ react.js:17311Mixin.perform @ react.js:17258ReactDefaultBatchingStrategy.batchedUpdates @ react.js:8842batchedUpdates @ react.js:15321ReactEventListener.dispatchEvent @ react.js:10336 react.js:20250Uncaught Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of
exports
.invariant @ react.js:20250instantiateReactComponent @ react.js:18268ReactCompositeComponentMixin._updateRenderedComponent @ react.js:6312ReactCompositeComponentMixin._performComponentUpdate @ react.js:6287ReactCompositeComponentMixin.updateComponent @ react.js:6216wrapper @ react.js:12868ReactCompositeComponentMixin.performUpdateIfNecessary @ react.js:6164ReactReconciler.performUpdateIfNecessary @ react.js:13667runBatchedUpdates @ react.js:15356Mixin.perform @ react.js:17245Mixin.perform @ react.js:17245assign.perform @ react.js:15313flushBatchedUpdates @ react.js:15374wrapper @ react.js:12868Mixin.closeAll @ react.js:17311Mixin.perform @ react.js:17258ReactDefaultBatchingStrategy.batchedUpdates @ react.js:8842batchedUpdates @ react.js:15321ReactEventListener.dispatchEvent @ react.js:10336
First component:
/** @jsx React.DOM */
var Second = require('components/second/view.jsx');
module.exports = React.createClass({
handlerClick: function () {
ReactDOM.render(
<Second />,
document.getElementById("app-container")
)
},
render: function() {
return (
<input type="button" value="COMPONENT 1" onClick={this.handlerClick} />
)
}
});
Second Component:
/** @jsx React.DOM */
var First = require('components/first/view.jsx');
module.exports = React.createClass({
handlerClick: function () {
ReactDOM.render(
<First />,
document.getElementById("app-container")
)
},
render: function() {
return (
<input type="button" value="COMPONENT 2" onClick={this.handlerClick} />
)
}
});
Index.js
ReactDOM.render(
<div>
<First />
</div>,
document.getElementById("app-container")
);
Hooks version (React 16.8+):
Minimal version.
import React, { useState } from 'react';
export default function App() {
const [toggle, setToggle] = useState(true);
const toggleChecked = () => setToggle(toggle => !toggle);
return (
<div>
{toggle && <First /> }
{!toggle && <Second /> }
<button type="button" onClick={this.toggleChecked}>
Toggle
</button>
</div>
);
}
Here's a very simple solution creating a SwitchComponents
component:
// SwitchComponents.js:
import React from 'react';
export default function SwitchComponents({ active, children }) {
// Switch all children and return the "active" one
return children.filter(child => child.props.name == active)
}
And import it in your app:
// App.js
import SwitchComponents from './components/SwitchComponents';
export default function App() {
const [activeComponent, setActiveComponent] = useState("questions")
return (
<SwitchComponents active={activeComponent}>
<Home name="home" />
<Instructions name="instructions" />
<FileboxContainer name="filebox" />
<Questions name="questions" />
</SwitchComponents>
)
}
You only ever call ReactDOM.render()
when you mount the application. Once mounted, you never call ReactDOM.render()
again on the same mount point. [*see update below.]
Remember that your view is a function of your props and state. To change your view, trigger a change in state.
I suggest something like this:
var Parent = React.createClass({
getInitialState: function () {
return {
active: 'FIRST'
};
},
handleClick: function () {
var active = this.state.active;
var newActive = active === 'FIRST' ? 'SECOND' : 'FIRST';
this.setState({
active: newActive
});
},
render: function () {
var active = this.state.active;
return (
<div>
{active === 'FIRST' ? (
<First />
) : active === 'SECOND' ? (
<Second />
) : null}
<button type="button" onClick={this.handleClick}>
Toggle
</button>
</div>
);
}
});
And make the Parent the root node. i.e.
ReactDOM.render(<Parent />, document.getElementById('app-container'));
UPDATE: I've since learned you can call ReactDOM.render()
multiple times on the same mount point. This would typically be in the same place you initialise the application. Nevertheless, you certainly don't call ReactDOM.render()
from inside a React component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With