3

I have three components:

const Comp0 = () => <div>1</div>;
const Comp1 = () => <div>2</div>;
const Comp2 = () => <div>3</div>;

I have also a class, with state:

state = { activeComponent: 0 }

This activeComponent can be changed by user to 1, 2 or 0.

In render, I have:

return (
   {React.createElement(`Comp${this.state.activeComponent}`)};
}

It should work... theoretically. However - Im getting a really weird error. Two errors.

  1. Warning: <Comp0 /> is using uppercase HTML. Always use lowercase HTML tags in React.

  2. Warning: The tag <Comp0> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.

How is that possible that they appear simultaneously?

7
  • 1
    I'm pretty sure React.createElement(...) creates a DOM element and not a React component Commented Mar 16, 2018 at 17:14
  • @devk, no it creates a React component too, reactjs.org/docs/react-without-jsx.html Commented Mar 16, 2018 at 17:16
  • Why do you do it like this? Why don't you just use an if statement? Commented Mar 16, 2018 at 17:16
  • @JanickFischer What if I have 100 components? Using if for every case would be an overkill. Commented Mar 16, 2018 at 17:17
  • If you have 100 components, you may as well put them into a state array, as I show below. How else would you keep track of them? Commented Mar 16, 2018 at 17:18

3 Answers 3

11

You could simply render the dynamic tag like

const Tag = `Comp${this.state.activeComponent}`;
return (
   <Tag />
}

According to the docs:

You cannot use a general expression as the React element type. If you do want to use a general expression to indicate the type of the element, just assign it to a capitalized variable first.

In your case it doesn't work because, you are passing the string name to React.createElement whereas for a React Component you need to pass the component like

React.createElement(Comp0);

and for a normal DOM element you would pass a string like

React.createElement('div');

and since you write

`Comp${this.state.activeComponent}`

what you get is

React.createElement('Comp0')

which isn't quite understandable to react and it throws a warning

Warning: <Comp0 /> is using uppercase HTML. Always use lowercase HTML tags in React.

Sign up to request clarification or add additional context in comments.

Comments

2

If you were to create a custom component element with React.createElement, you have to pass the direct class/function, instead of its name (that's only for DOM elements), to it, e.g. React.createElement(Shoot0) instead of React.createElement('Shoot0');

You can circumvent the issue by putting the components you intend for in array and index them

const Shoot0 = () => <div>1</div>;
const Shoot1 = () => <div>2</div>;
const Shoot2 = () => <div>3</div>;

const Shoots = [Shoot0, Shoot1, Shoot2];

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      activeComponent: 0
    };
  }
  
  componentDidMount() {
    setInterval(() => {
      this.setState((prevState) => {
        return {
          activeComponent: (prevState.activeComponent + 1) % 3
        }
      })
    }, 1000)
  }
  
  render() {
    return React.createElement(Shoots[this.state.activeComponent])
  }
}

ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Comments

1

You can just do a function with a mapping like this:

const stateArray = [Comp0, Comp1, Comp2];
const getComp = (Comp) => <Comp>
const getCompFromArray = (i) => getComp(stateArray[i]);

3 Comments

I dont feel rly well with that solution... I would rather avoid putting every imported component inside an array.
For what reason? You aren't globally calling components, are you? What's the usecase? Also, have you looked at @Shubham 's solution?
Ye, I import them from global scope

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.