Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switching between components in React JS

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:20250

Uncaught 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")
);
like image 685
user5512965 Avatar asked Dec 03 '15 23:12

user5512965


3 Answers

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>
  );
}
like image 88
Hunter Avatar answered Nov 18 '22 04:11

Hunter


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>
  )
}
like image 41
Maycow Moura Avatar answered Nov 18 '22 02:11

Maycow Moura


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.

like image 10
David L. Walsh Avatar answered Nov 18 '22 03:11

David L. Walsh