user5512965
user5512965

Reputation: 151

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")
);

Upvotes: 11

Views: 37622

Answers (4)

Maycow Moura
Maycow Moura

Reputation: 6949

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>
  )
}

Upvotes: 9

Hunter
Hunter

Reputation: 3362

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>
  );
}

Upvotes: 2

DilipCoder
DilipCoder

Reputation: 578

in the render function ternary operator will be replaced simply and the full answer will be like:

    /** @jsx React.DOM */

    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 /> }
                    <button type="button" onClick={this.handleClick}>
                        Toggle
                    </button>
                </div>
            );

         }
    });


  ReactDOM.render(<Parent />, document.getElementById('app-container'));

Rest will be same.

Upvotes: 1

David L. Walsh
David L. Walsh

Reputation: 24818

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.

Upvotes: 10

Related Questions