Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic JSX element/tag names

I'm just wanted to know if there is a best practice approach to rendering an element with ReactJS dynamically

Consider these scenarios:

(1) Parameter Factory component:
A parameterised factory component whose job is to render a component based on a string parameter, is there a way of doing so without having to revert to React.createElement?

<pre><code>// The following doesn't work
class Quiz extends React.Component{
  constructor (props){
    super (props);
    this.state = {
      questionText: '',
      correctAnswer: [],
      assetType: ['DragNDrop','MultipleChoice','MatchPairs']
    }
  }
  render(){
    const { questionText, correctAnswer } = this.state;
    return <{this.state.assetType[this.props.typeIndex] />;
  }
}
</code></pre>

(2)Dynamic HTML tags:
Render a different HTML header tag based on an integer input. For this, I initially tried to use Template strings but had to resort to conditional rendering.

<pre><code>// No joy with Template strings
render (){
  <{`h${this.state.headerSize}`}>
    {this.state.headerText}
  </ {`h${this.state.headerSize}`}>
}

I like using JSX, and it would be nice to be able to use dynamic element names with it for consistency.

I'm also aware that:

assetType: ['DragNDrop','MultipleChoice','MatchPairs']

can be stored as:

assetType: [<DragNDrop />,<MultipleChoice />, <MatchPairs />]

which will work.

One issue I have with the array of JSX elements is how to store the these JSX elements in a DB? I'm guessing I'd have to store them as Strings but then how to use them when pull backed form the DB?

Can anyone suggest any working and best practice approaches to these issues?

like image 834
Pineda Avatar asked Mar 10 '23 04:03

Pineda


1 Answers

Regarding dynamic HTML tags:

EDIT:
As the docs suggest, Dynamic types can be used at runtime if first assign to a capitalised variable first:

class Quiz extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            questionText: '',
            correctAnswer: [],
            assetType: ['DragNDrop', 'MultipleChoice', 'MatchPairs']
        }
    }
    render() {
        const ElementNameStartsWithCapitalLetter = this.state.assetType[0];
           // ^ -- capital letter here, ensure this works when used in JSX
        return <ElementNameStartsWithCapitalLetter />;
    }
}

This is due to the fact that User Defined JSX Components Must BE Capitalized.



PREVIOUS SOLUTIONS:

Using React.createElement:

class Quiz extends React.Component{
  constructor (props){
    super (props);
    this.state = {
      questionText: '',
      correctAnswer: [],
      assetType: ['DragNDrop','MultipleChoice','MatchPairs']
    }
  }
  render(){
    const { questionText, correctAnswer } = this.state;
    {React.createElement(
      [this.props.typeIndex],
      {...questionText, ...correctAnswer}
    );}
  }
}

Using conditional rendering:

// Conditional rendering works, but yuck!
// One condition per state works
// <b>but can be unnecessarily verbose</>
getHeader() {
  switch(this.state.headerSize){
    case 1:
      return <h1>{ this.state.headerText }; <h1>
    case 2:
      return <h2>{ this.state.headerText }<h2>
    .
    .
    .
    default:
      return null;
  }
}

render (){
  return { this.getHeader() }; // bound correctly in constructor of course :)
}
like image 163
Pineda Avatar answered Mar 23 '23 23:03

Pineda